Fix: Nginx Not Connecting to Gunicorn (Connection Refused)
If Nginx cannot connect to Gunicorn and you are seeing connection refused, 502, or upstream errors, this guide shows you how to restore the backend connection step-by-step. The goal is to verify Gunicorn is running, confirm Nginx points to the correct socket or port, and fix permission or service issues that block the upstream.
Quick Fix / Quick Setup
Run the basic validation sequence first:
sudo systemctl restart gunicorn
sudo systemctl restart nginx
sudo systemctl status gunicorn --no-pager
sudo ss -ltnp | grep 8000
sudo ls -l /run/gunicorn.sock
sudo nginx -t
Use the port check if Gunicorn is bound to 127.0.0.1:8000. Use the socket check if Nginx is configured with unix:/run/gunicorn.sock. If Gunicorn is not active or the socket does not exist, fix the Gunicorn service first.
What’s Happening
Nginx proxies requests to Gunicorn through either a Unix socket or a local TCP port. A connection refused error means Nginx reached the target path or address, but no process was listening there, the socket path was wrong, or access to the socket was blocked. In most cases, the issue is a stopped Gunicorn service, a bind mismatch, or socket permission problems.
Step-by-Step Guide
- Confirm the exact Nginx upstream error
Run:bashsudo tail -n 50 /var/log/nginx/error.log
Look for messages such as:connect() failed (111: Connection refused) while connecting to upstreamNo such file or directoryPermission denied
- Check whether Gunicorn is running
Run:bashsudo systemctl status gunicorn --no-pager
If it is inactive or failed:bashsudo systemctl restart gunicorn sudo systemctl status gunicorn --no-pager - Review Gunicorn logs for startup failures
Run:bashsudo journalctl -u gunicorn -n 100 --no-pager
Fix any of these before continuing:- import errors
- missing dependencies
- invalid virtualenv path
- wrong
WorkingDirectory - bad WSGI module path
If Gunicorn fails to start at all, use Flask Gunicorn Service Failed to Start. - Verify how Gunicorn is bound
Inspect the systemd unit:bashsudo systemctl cat gunicorn
Look for a bind target such as:bash--bind unix:/run/gunicorn.sock
or:bash--bind 127.0.0.1:8000 - Match the Nginx upstream to the Gunicorn bind target
If using a Unix socket, your Nginx config should point to the same socket path.
Example:nginxserver { listen 80; server_name your-domain.com; location / { proxy_pass http://unix:/run/gunicorn.sock; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
If using TCP:nginxserver { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } - Test whether the bind target actually exists
For a Unix socket:bashsudo ls -l /run/gunicorn.sock
For TCP:bashsudo ss -ltnp | grep 8000
If nothing is listening and no socket exists, Gunicorn is not bound correctly. - Fix the Gunicorn systemd service if needed
Example systemd service:ini[Unit] Description=Gunicorn instance for Flask app After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/srv/app ExecStart=/srv/app/venv/bin/gunicorn --workers 3 --bind unix:/run/gunicorn.sock wsgi:app [Install] WantedBy=multi-user.target
Validate:ExecStartpoints to the correct Gunicorn binary inside the virtualenvWorkingDirectorypoints to the app directorywsgi:appmatches your app entrypoint
- Reload systemd and restart Gunicorn after changes
Run:bashsudo systemctl daemon-reload sudo systemctl restart gunicorn sudo systemctl status gunicorn --no-pager - Check socket ownership and permissions
Run:bashsudo ls -l /run/gunicorn.sock
Nginx must be able to access the socket. If Nginx runs aswww-data, the socket owner/group must allow access.
Example output:bashsrw-rw---- 1 www-data www-data 0 Apr 21 12:00 /run/gunicorn.sock
If permissions are wrong, adjust the Gunicorn serviceUserandGroup, or configure socket creation appropriately. - Validate the active Nginx configuration
Run:bashsudo nginx -t
Also check for stale references:bashsudo grep -R "proxy_pass\|upstream\|gunicorn.sock\|127.0.0.1:8000" /etc/nginx/sites-enabled /etc/nginx/nginx.conf
Fix:- wrong socket path
- wrong port
- duplicate old upstream blocks
- syntax errors
- Reload Nginx
After confirming Gunicorn is reachable:bashsudo systemctl reload nginx - Test the application locally first
For TCP:bashcurl -I http://127.0.0.1:8000
For a socket-based setup, confirm the socket exists first, then test through Nginx:bashcurl -I http://localhost - Test the full request path
Run:bashcurl -I http://your-domain
or:bashcurl -I http://server-ip
A200,301,302, or valid app response confirms Nginx can reach Gunicorn. - Check local security policy if the configuration still looks correct
On hardened systems, SELinux or other local policy controls can block access to sockets or local ports. Review those controls if Gunicorn is running and Nginx still cannot connect. - If the error presents as a 502 instead of connection refused
Use the broader upstream troubleshooting page at Fix Flask 502 Bad Gateway (Step-by-Step Guide).
Common Causes
- Gunicorn service is stopped or crashed → Nginx has no backend to connect to → Restart Gunicorn and inspect logs with
journalctl. - Nginx points to the wrong Unix socket path → The configured socket file does not exist → Update
proxy_passor Gunicorn--bindso both use the same path. - Nginx points to the wrong TCP port → Nothing is listening on that port → Change Nginx upstream or Gunicorn bind to match.
- Gunicorn failed to start because of import or app path errors → Service never opens the socket or port → Fix
ExecStart,WorkingDirectory, and the WSGI module path. - Socket permission denied → Gunicorn created the socket but Nginx cannot read or connect to it → Adjust service user, group, and socket permissions.
- Virtualenv or binary path is wrong in systemd → Gunicorn command fails during startup → Point
ExecStartto the correct Gunicorn binary inside the virtualenv. - Old Nginx config still loaded → Nginx proxies to an outdated socket or port → Test config and reload Nginx.
- Runtime directory cleanup removed the socket → Nginx references a stale path after reboot or restart → Ensure the service recreates the socket on startup.
- Firewall or local security policy blocks access to the bind target → Nginx cannot reach Gunicorn on TCP or local socket policy → Review local security configuration.
Debugging Section
Check these commands in order:
sudo tail -n 100 /var/log/nginx/error.log
sudo tail -n 100 /var/log/nginx/access.log
sudo systemctl status gunicorn --no-pager
sudo journalctl -u gunicorn -n 100 --no-pager
sudo journalctl -u nginx -n 100 --no-pager
sudo nginx -t
sudo systemctl cat gunicorn
sudo ss -ltnp | grep 8000
sudo ls -l /run/gunicorn.sock
ps aux | grep gunicorn
curl -I http://127.0.0.1:8000
curl -I http://localhost
sudo grep -R "proxy_pass\|upstream\|gunicorn.sock\|127.0.0.1:8000" /etc/nginx/sites-enabled /etc/nginx/nginx.conf
What to look for:
- Nginx error log contains the exact upstream target and failure type
- Gunicorn logs show whether startup failed before binding
- The expected socket file exists if using Unix sockets
- The expected port is listening if using TCP
- Nginx config matches the exact Gunicorn bind target
- The loaded systemd unit uses the correct app path and virtualenv binary
Checklist
- Gunicorn service is active and running under systemd
- Gunicorn is bound to the same socket path or TCP port used by Nginx
-
/run/gunicorn.sockexists if using a Unix socket - Nginx has permission to access the Gunicorn socket
-
nginx -tpasses without errors - Nginx has been reloaded after config changes
-
curlto the domain returns an application response instead of502orconnection refused
Related Guides
- Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)
- Fix Flask 502 Bad Gateway (Step-by-Step Guide)
- Flask Gunicorn Service Failed to Start
- Flask Production Checklist (Everything You Must Do)
FAQ
What Nginx error usually confirms this issue?
Look for connect() failed (111: Connection refused) while connecting to upstream in /var/log/nginx/error.log.
How do I know whether Gunicorn uses a socket or a port?
Check the Gunicorn startup command or systemd ExecStart for a --bind value such as unix:/run/gunicorn.sock or 127.0.0.1:8000.
Why does restarting Nginx not fix it?
Because the backend problem is usually Gunicorn not running, failing on startup, or listening on a different target.
Can I switch from a socket to a TCP port to debug faster?
Yes. Binding Gunicorn to 127.0.0.1:8000 can simplify testing with ss and curl as long as Nginx is updated to proxy to that port.
What should I check after a server reboot?
Verify the Gunicorn service starts automatically, recreates the socket if used, and that Nginx still points to the correct path or port.
Final Takeaway
This issue is usually not in Nginx itself. It is almost always a mismatch between the Nginx upstream target and the actual Gunicorn bind, or a Gunicorn startup failure. Validate Gunicorn first, then confirm the socket or port matches Nginx, then reload and test the full request path.