gunicorn in a Python virtual environment, run gunicorn --bind unix:/run/myapp/gunicorn.sock app:app (or mysite.wsgi for Django) as a systemd service, then add an Nginx server block with proxy_pass http://unix:/run/myapp/gunicorn.sock;. Nginx handles the domain, HTTPS and static files; Gunicorn runs your Python code.
Before you start
- Install Nginx and point your domain at the server.
- Run the app as a normal user, not
root. This guide uses the useralexand the folder/home/alex/myapp. - Flask’s and Django’s built-in servers (
flask run,runserver) are for development only. Gunicorn is a production WSGI server: it runs several worker processes that handle requests in parallel.
How to install the app and Gunicorn
1
Install Python tools
python3 already includes venv.2
Create a virtual environment
django instead of flask, plus your requirements.txt with ./venv/bin/pip install -r requirements.txt.3
Test Gunicorn by hand
For a Flask app in For a Django project called In a second SSH window,
app.py with app = Flask(__name__):mysite:curl http://127.0.0.1:8000 should return your page. Press Ctrl+C to stop.module:variable: app:app means “the app object in app.py”. Gunicorn’s docs suggest (2 × CPU cores) + 1 workers as a starting point.
How to run Gunicorn as a systemd service
Create/etc/systemd/system/myapp.service:
RuntimeDirectory=myappcreates/run/myappat each start.Group=www-dataand--umask 007make the socket readable by Nginx, which runs aswww-dataon Ubuntu and Debian.- For Django, replace
app:appwithmysite.wsgi.
How to configure Nginx
Create/etc/nginx/sites-available/myapp:
proxy_params (Ubuntu / Debian) sets the Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto headers. Enable and reload:
sudo certbot --nginx -d example.com -d www.example.com (Certbot guide).
Django settings for production
Insettings.py:
/var/www because Ubuntu home folders are not readable by other users, so Nginx could not serve them from /home/alex.
AlmaLinux / Rocky: Nginx runs as
nginx, there is no proxy_params file, and SELinux limits what Nginx may connect to. The simplest setup is to bind Gunicorn to 127.0.0.1:8000, use proxy_pass http://127.0.0.1:8000; with the proxy_set_header lines from the reverse proxy guide, and run sudo setsebool -P httpd_can_network_connect 1.How to check it worked
502 Bad Gateway, read the reason in sudo tail /var/log/nginx/error.log and the app’s own log with sudo journalctl -u myapp -n 50. See fix 502 Bad Gateway.
How to deploy an update
./venv/bin/python manage.py migrate and collectstatic before the restart.
Frequently asked questions
Do I need Nginx in front of Gunicorn?
Yes, for public sites. Gunicorn’s documentation strongly recommends a proxy such as Nginx, which buffers slow clients, serves static files and handles HTTPS.Django shows “Bad Request (400)”. Why?
The domain is missing fromALLOWED_HOSTS. Add it and restart the service.
How do I see Gunicorn errors?
sudo journalctl -u myapp -f follows the service log. Gunicorn writes startup errors, such as a wrong module:variable, there.
Can I use uWSGI or Uvicorn instead?
Yes. For async frameworks such as FastAPI, run Uvicorn workers. The systemd and Nginx parts stay the same.Related guides
VPS and dedicated server guides
Every server guide in one place.
Nginx reverse proxy
Proxy headers explained.
Create a systemd service
Units and restart rules.
Install PostgreSQL
A database for Django.
Fix 502 Bad Gateway
When Nginx can’t reach Gunicorn.
Checked against the official Gunicorn deployment documentation and Django deployment checklist on 4 November 2026; Gunicorn 26.2.0 with Flask 3.1 and Django 6.1 tested on Ubuntu 24.04 LTS behind Nginx 1.24.0 on a Unix socket, and the systemd unit checked with
systemd-analyze verify. Commands can differ on other distributions or releases.Need Extra Help?
If you encounter any issues, our support team is ready to assist:- Live Chat: Quick assistance via our website.
- Support Ticket: Open a Ticket
- Discord: Join our Community
- Email: support@freakhosting.com
Last Updated: November 2026