How to Fix Nginx 503 Service Unavailable

Nginx returns 503 when every upstream is marked unhealthy or the backend can't respond in time. A mismatch between the health check path in nginx.conf and the path the app actually exposes is the most common silent trigger - nginx declares the backend down before a real user even connects.

DevOps Engineernginxreverse-proxytimeouts

What a 503 Service Unavailable actually means

Unlike a 502 (no connection), nginx returns 503 when the upstream exists but is considered unavailable - either the health check keeps failing, or all upstream connections are occupied / timed out. The backend can be running fine and the error still fires.

Step 1: read the error log

tail -50 /var/log/nginx/error.log

Two messages that commonly appear together:

upstream timed out (110: Connection timed out) while reading response header
from upstream, upstream: "http://127.0.0.1:8080/"

no live upstreams while connecting to upstream

The first line tells you how each request failed; the second tells you nginx has marked every backend server down.

Step 2: hit the backend directly

curl -v http://127.0.0.1:8080/
time curl http://127.0.0.1:8080/  # check if it's just slow

If the backend answers correctly but takes a few seconds, the issue is proxy_read_timeout - not the backend itself.

Step 3: check the health check path

Open /etc/nginx/nginx.conf and find the health check location or upstream health_check directive:

upstream backend {
    server 127.0.0.1:8080;
}

server {
    location /health {         # <- the path nginx polls
        proxy_pass http://backend;
    }
}

Now check what path the app actually exposes:

curl http://127.0.0.1:8080/healthz   # returns 200
curl http://127.0.0.1:8080/health    # returns 404

A path mismatch (/health in nginx vs /healthz on the app) means every health probe returns 404, nginx marks the upstream unhealthy, and all requests get 503 - even while the app handles real traffic fine.

Step 4: fix both problems and reload

Correct the health check path and raise the read timeout to something realistic (30-60 seconds is common; 1 second is almost never right):

upstream backend {
    server 127.0.0.1:8080;
}

server {
    proxy_read_timeout 30s;    # was 1s - too tight for any real request

    location / {
        proxy_pass http://backend;
    }

    location /healthz {        # matches the app's actual endpoint
        proxy_pass http://backend;
    }
}

Then validate and reload - no full restart needed:

nginx -t           # check config syntax
nginx -s reload    # graceful reload
curl http://localhost/   # should return 200

Why these two bugs compound

A too-short proxy_read_timeout causes individual requests to fail, which makes nginx increment the upstream's failure count. If failures cross the threshold, nginx stops sending new requests to that upstream entirely - turning intermittent timeouts into a complete outage. Fix the health path first (so nginx trusts the upstream again), then fix the timeout (so slow requests don't re-trigger it).

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What causes nginx 503 Service Unavailable?

Nginx returns 503 when it marks every upstream server as unhealthy (usually because health checks are failing) or when all upstream connections are timing out. A health check path mismatch - where nginx polls /health but the app exposes /healthz - is a common silent trigger.

How do I find what's causing the nginx 503?

Run tail -50 /var/log/nginx/error.log to see upstream timeout or 'no live upstreams' messages, then curl the backend directly to confirm it's actually running. Compare the health check path in nginx.conf to the paths the app exposes.

What should proxy_read_timeout be set to in nginx?

Typically 30-60 seconds for a web application. The default is 60s. A value of 1s is almost always too tight and will cause timeouts on anything but the fastest responses, which nginx counts as upstream failures.

How do you fix a 503 Service Unavailable in nginx?

A 503 means nginx cannot get a healthy response from the upstream. Confirm the backend is running and listening, check the health-check path matches, and raise proxy_read_timeout if the upstream is just slow. Then reload nginx.

Keep learning

Fix the Nginx 502 Bad GatewayDevOps projectRecover a Crashed Linux ServiceDevOps projectCorrect a Postgres Port MismatchDevOps projectDevOps roadmapStep by step to hiredDevOps interview questionsSTAR answersAll DevOps projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →