Skip to main content
To deploy a Flask or Django app with Gunicorn and Nginx, install the app and 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 user alex and 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

On AlmaLinux / Rocky, python3 already includes venv.
2

Create a virtual environment

For Django, install 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 app.py with app = Flask(__name__):
For a Django project called mysite:
In a second SSH window, curl http://127.0.0.1:8000 should return your page. Press Ctrl+C to stop.
The format is 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=myapp creates /run/myapp at each start.
  • Group=www-data and --umask 007 make the socket readable by Nginx, which runs as www-data on Ubuntu and Debian.
  • For Django, replace app:app with mysite.wsgi.
Start it:

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:
Then add HTTPS with sudo certbot --nginx -d example.com -d www.example.com (Certbot guide).

Django settings for production

In settings.py:
Collect static files into the folder Nginx serves:
Static files live under /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

You should see your app’s response. If Nginx returns 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

For Django, also run ./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 from ALLOWED_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.

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:

Save on Your Hosting

Ready to upgrade your hosting? Use code KB20 at checkout for 20% off your first order!

Last Updated: November 2026