502 means nginx reached for your upstream and got nothing usable back. The error log names the exact reason — it is rarely vague.
Upstream socket/port mismatch, or the app crashed. Log shows 'connect() failed (111: Connection refused)'.
httpd is not allowed to make network connections by default. Log shows 'Permission denied' on connect.
Long requests exceed proxy_read_timeout and get killed, surfacing as 502/504.
tail -50 /var/log/nginx/error.log
curl -v http://127.0.0.1:8080/healthz # match the upstream line
setsebool -P httpd_can_network_connect 1
proxy_read_timeout 120s;
proxy_connect_timeout 10s;
Connection refused = wrong place or dead app. Permission denied = SELinux. Timeout = slow app. The error log always tells you which of the three you have.
The upstream accepts light traffic but exhausts under load: worker/thread pool limits, connection queue, or the app crashing on concurrency. The nginx error log distinguishes 'connect() failed' (upstream refused) from 'upstream prematurely closed' (app died mid-request).
Rarely — misconfigured proxy_pass targets (wrong port, wrong protocol header) can produce it, but the cause is still a failed upstream conversation. Fix the upstream path first; nginx is almost always the messenger.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.