After restarting a Django server, 500 and 502 errors from Nginx and Gunicorn usually mean the Gunicorn workers didn’t come back up correctly or Nginx can’t reach them. The fix is to check Gunicorn’s socket and process state, restart it in the right order, and verify Nginx’s proxy configuration points to the live socket.
What These Errors Actually Mean
A 502 Bad Gateway from Nginx means Nginx couldn’t get a valid response from Gunicorn. The upstream connection failed, timed out, or Gunicorn returned nothing. A 500 Internal Server Error usually means Gunicorn is running but the Django application itself crashed, often due to missing environment variables, a failed database connection, or a code error that only appears after a restart.
Both errors commonly appear right after a server reboot, a code deployment, or a manual restart of Gunicorn or Nginx. The key is to figure out which layer is failing.
Why Restarts Trigger These Errors
When you restart a Django project, several things can go wrong:
- Gunicorn doesn’t start automatically – if you use a systemd service, the service might fail to start due to a misconfigured working directory, missing virtual environment path, or a Python dependency that isn’t installed.
- The socket file is stale – Gunicorn creates a Unix socket (often
/run/gunicorn.sock). If the process is killed but the socket file remains, Nginx will try to connect to a dead file and get a 502. - Environment variables are missing – Django settings often rely on
DJANGO_SECRET_KEY,DB_NAME, or other variables. If the Gunicorn service doesn’t load them, Django raises an exception on startup, causing a 500. - Database or cache is down – after a restart, the database may still be starting up. Django tries to connect and fails, returning a 500.
How to Diagnose the Problem
Start by checking the Gunicorn service status and logs. On a typical Linux system with systemd:
- Run
sudo systemctl status gunicorn– this shows if the service is active, failed, or exited. - Check logs with
sudo journalctl -u gunicorn --since "5 minutes ago"– look for Python tracebacks or “Connection refused” messages. - Check Nginx logs at
/var/log/nginx/error.log– a 502 often includes the exact upstream error, such as “connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)”. - Manually test the socket:
curl --unix-socket /run/gunicorn.sock http://localhost– if you get an HTML response, Gunicorn is fine and the issue is Nginx config. If you get “Connection refused”, Gunicorn isn’t listening.
Step-by-Step Fix for Common Causes
Here’s the order to fix the most common restart-related issues:
- Restart Gunicorn properly – Use
sudo systemctl restart gunicorn. If you’re not using systemd, kill the old process (pkill gunicorn) and start a new one with the same command you originally used. Make sure the working directory is your Django project folder. - Remove stale socket – If Gunicorn fails to start because the socket file exists, delete it:
sudo rm /run/gunicorn.sock, then start Gunicorn again. The socket should be recreated. - Verify Nginx proxy config – Open your Nginx site config (e.g.,
/etc/nginx/sites-available/myproject) and ensure theproxy_passpoints to the exact socket path Gunicorn is using. For example:proxy_pass http://unix:/run/gunicorn.sock;. If you changed the socket path in Gunicorn, update Nginx too. - Test Gunicorn directly – Run Gunicorn in the foreground with
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000(replace with your app’s WSGI module). If it starts without errors, the issue is in your service file or environment. If it crashes, you’ll see the traceback immediately. - Check file permissions – Nginx runs as user
www-data(ornginx). Ensure that user has read access to the socket file:ls -l /run/gunicorn.sockshould showsrw-rw----with group ownership matching Nginx’s group. If not, adjust thegroupin your Gunicorn systemd service or the socket permissions. - Restart Nginx after fixing Gunicorn – Once Gunicorn is up, run
sudo systemctl restart nginxto clear any cached upstream failures.
Preventing 500 Errors from Django Code
If you get a 500 (not 502), Gunicorn is running but Django is throwing an exception. Common causes after a restart:
- Missing
SECRET_KEYor other environment variables – check your systemd service file’sEnvironmentlines or your.envfile. - Database migrations not applied – run
python manage.py migratebefore starting Gunicorn. - Static files not collected – if you use
whitenoiseor Nginx to serve static files, runpython manage.py collectstaticafter code changes. - Python dependencies missing – activate your virtual environment and run
pip install -r requirements.txt.
To see the actual error, temporarily set DEBUG = True in your Django settings and restart Gunicorn. The 500 page will show a traceback. Remember to set DEBUG = False again and restart.
Systemd Service Example for Reliable Restarts
If you’re using systemd, a minimal service file for Gunicorn looks like:
[Unit]
Description=Gunicorn for my Django project
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/project
EnvironmentFile=/path/to/.env
ExecStart=/path/to/venv/bin/gunicorn myproject.wsgi:application --bind unix:/run/gunicorn.sock
Restart=on-failure
[Install]
WantedBy=multi-user.target
Note the Restart=on-failure line – this makes systemd restart Gunicorn automatically if it crashes, which helps avoid 502s after unexpected restarts. Also, the EnvironmentFile loads your environment variables, preventing the missing-variable 500s.
Frequently Asked Questions
Why do I get 502 after every reboot?
Most likely Gunicorn isn’t set to start on boot. Run sudo systemctl enable gunicorn to make the service start automatically. Also check that your database service (like PostgreSQL) starts before Gunicorn – you can add After=postgresql.service in the [Unit] section.
Should I use a Unix socket or a TCP port?
Unix sockets are faster and more secure because they don’t expose a network port. Use a socket file like /run/gunicorn.sock for local Nginx communication. If you need to connect from another machine, use a TCP port like 127.0.0.1:8000 instead.
How do I restart Gunicorn without dropping requests?
Use sudo systemctl reload gunicorn if you have a systemd service with ExecReload=/bin/kill -s HUP $MAINPID. This sends a HUP signal that gracefully restarts workers without closing the listening socket. If you don’t have that, you can use kill -HUP on the master process ID. Nginx doesn’t need to be restarted for this.
What if Nginx shows 502 but Gunicorn is running?
Check if the socket path matches between Nginx and Gunicorn. Also verify Nginx’s user (www-data) has permission to access the socket. Run sudo -u www-data curl --unix-socket /run/gunicorn.sock http://localhost to test. If that fails, it’s a permission issue.
Why do my errors disappear after I run Gunicorn manually but not via systemd?
Usually it’s an environment difference. When you run manually, your shell has variables like PATH and DJANGO_SECRET_KEY set. Systemd doesn’t inherit those. Use EnvironmentFile in the service file to load the same variables.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Valheim Server Not Showing in Community List Fixes
- Soundbar vs 2.1 Speaker System: Which Is Better?
- DisplayPort vs HDMI: Which Cable Should You Use?
- Survive Lava for Brainrot Tier List170 points
- MySQL Dump Import: Fix Common Errors
- Mouse Moving on Its Own? Causes & Fixes