Deploy Error Decoder

502 Bad Gateway after deploy — causes and fixes

Quick answer: A 502 Bad Gateway right after a deploy means your reverse proxy reached the server but got no valid response from your app — it crashed on startup, isn't listening yet, or is bound to the wrong host/port. Check the proxy's error log, confirm the app is listening with ss -ltnp, and curl it directly on its port to bypass the proxy.

What you'll see in the logs

The browser shows a bare "502 Bad Gateway". The real clue is in the proxy's error log:

# /var/log/nginx/error.log
connect() failed (111: Connection refused) while connecting to upstream,
  upstream: "http://127.0.0.1:3000/", host: "example.com"
# or, with multiple backends:
no live upstreams while connecting to upstream

Connection refused means nothing is listening on the upstream address. no live upstreams means every backend failed its health check. Both point at the app, not the proxy.

The four common causes

The app crashed on startup

A bad env var, a failed migration, or a syntax error killed the process. The proxy is up but there's nothing behind it.

It isn't listening yet

Traffic shifted before the new instance finished booting — a race between "old stopped" and "new ready".

Wrong host or port

The app binds 127.0.0.1:3000 but the proxy points at :8080 — or it listens only on localhost inside a container.

The app is too slow to respond

A long startup or blocked request exceeds the proxy's proxy_read_timeout, surfacing as a 502 (or 504).

Diagnose it in three steps

1

Confirm the app is actually listening

ss -ltnp | grep 3000
# Nothing returned? The process is down or on a different port.
2

Bypass the proxy — curl the app directly

curl -v http://127.0.0.1:3000/healthz
# Works here but 502 through the proxy? Proxy/address mismatch.
# Fails here too? The app itself is down — check its logs.
3

Read the app's own startup logs

A common container gotcha is binding to localhost instead of all interfaces:

# Inside a container, bind 0.0.0.0 — not 127.0.0.1
app.listen(3000, '0.0.0.0');
The real fix

Never shift traffic to an unready app

A 502 on deploy is a sequencing bug: the proxy was given a backend that wasn't ready. The durable fix is a health check gate — the new instance only receives traffic after it answers a health endpoint, and the old instance keeps serving until then.

# app: a cheap readiness endpoint the deploy can poll
app.get('/healthz', (req, res) => res.sendStatus(200));

Deploy flow: start new instance → poll /healthz until 200 → add it to the proxy → drain and stop the old one. No window where the upstream is empty, no 502.

How Infraveil handles this

Health-gated deploys with a built-in gateway

Infraveil is a backend operations control plane that runs on your own servers. Its agent runs health checks and only marks a service live once it passes — so the gateway never routes to an instance that isn't ready, and the deploy that caused the 502 is gated before it reaches users. Every rollout pauses for your approval and is recorded in a tamper-evident audit trail, with one-click rollback.

Built-in traffic gateway routes only to instances that pass health checks
Crashed services are restarted automatically and surfaced in one log view
One-click rollback to the last known-good release

Frequently asked questions

What causes a 502 after a deploy?

Your reverse proxy accepted the request but got no valid response from your app. After a deploy that's usually a crash on startup, the app not listening yet, or a wrong host/port.

How do I debug an nginx 502?

Read /var/log/nginx/error.log for "Connection refused" or "no live upstreams", confirm the app is listening with ss -ltnp, then curl it directly on its port to bypass the proxy.

Why only right after deploying?

Traffic shifted before the new instance was ready, or the old one was stopped too early. Gate the cutover on a health check so there's no window with an empty upstream.

Is 502 a client or server error?

It's a server-side (5xx) error returned by the gateway, not your app. The client did nothing wrong; retrying may succeed once the upstream is healthy.