One thing I am learning as I continue my DevOps journey is that deploying an application successfully is only part of the job. Understanding what happens when that application fails is equally important.
In this hands-on lab, I deliberately broke my Node.js backend on an AWS EC2 Ubuntu server to see how NGINX behaves. The result was the classic 502 Bad Gateway.
I documented the full lab with screenshots on my blog:
Full tutorial here → https://blog.sirvickcloud9.online/2026/10/how-i-fixed-nginx-502-bad-gateway-error.html
What I Used
- AWS EC2 Ubuntu 22.04
- Node.js backend on port 3000
- NGINX 1.24.0 as reverse proxy (port 80 → 127.0.0.1:3000)
Request flow: Browser → NGINX → Node.js
What Happened
I stopped the Node.js service and hit my public IP. NGINX was still up but returned 502. That was the key lesson: 502 does not mean NGINX is down. It means NGINX cannot get a valid response from upstream.
How I Found the Real Cause
- Checked config syntax (this does NOT check if backend is running):
bash
sudo nginx -t
2. Checked real error:
sudo tail -n 50 /var/log/nginx/error.log
28906#28906: *10 connect() failed (111: Connection refused) while connecting to upstream, upstream: "http://127.0.0.1:3000/"
3. Verified with curl:
curl -i http://127.0.0.1:3000
curl -i http://127.0.0.1
I explained how I track Linux processes and debug ports with `ps aux | grep node`, `systemctl status` and `netstat -tulpn` in my earlier post about https://blog.sirvickcloud9.online/2025/09/understanding-linux-environment-real.html.
How I Restored and Verified
Instead of just restarting, I verified the actual HTTP response:
curl http://127.0.0.1:3000
Hello! The Node.js backend is working correctly.
curl -i http://127.0.0.1
HTTP/1.1 200 OK
Always verify with real HTTP response, not just `systemctl status`.
The Best Part: Testing systemd Auto-Restart
On Ubuntu, systemd can restart your app automatically:
in nginx-502-lab.service
Restart=on-failure
This is the same systemd approach I used when https://blog.sirvickcloud9.online/2025/10/my-github-actions-self-hosted-runner-kept.html — I fixed it by running it as a systemd service with `svc.sh`.
Check logs:
sudo journalctl -u nginx-502-lab -n 50 --no-pager
My logs showed:
Main process exited, code=killed, status=9/KILL
Scheduled restart job, restart counter is at 1.
Node.js application listening on 127.0.0.1:3000
After auto-restart, curl returned 200 OK again.
Key Lessons
- `sudo nginx -t` checks syntax only, not backend health
- Always check `error.log` for `Connection refused`
- Test both direct backend and through NGINX
- `Restart=on-failure` helps, but you still need monitoring
*I posted the full experiment with 4 screenshots, all commands, and production improvements on my blog:*
*→ Read full lab: https://blog.sirvickcloud9.online/2026/10/how-i-fixed-nginx-502-bad-gateway-error.html*
Thanks for reading SirVick DevOps Diary — I share practical labs from Port Harcourt as I learn AWS, Linux and DevOps.
---
If this saved you time fixing 502, support my next lab: https://app.raenest.com/invoice/payment/RNMX5R3WP
Top comments (0)