The standard way to run Django in production is Nginx + Gunicorn + Django. Each piece has a specific job; understanding the flow makes deployment and debugging much easier.
The request flow
A browser request hits Nginx first. Nginx serves static files directly and forwards everything else to Gunicorn, which runs your Django code and returns a response back through Nginx to the user.
What each does
- Nginx — fast web server and reverse proxy. Handles TLS/HTTPS, serves static and media files, buffers slow clients, and shields Gunicorn from the open internet.
- Gunicorn — the WSGI application server. Runs multiple worker processes that execute your Django code. It's efficient but not designed to face the internet directly.
- Django — your application logic, running inside Gunicorn workers.
Why not just Django's runserver?
Django's built-in server is for development only — it's single-threaded and not hardened. Gunicorn provides concurrency and stability; Nginx provides performance and security.
Tuning workers
A common starting point is (2 × CPU cores) + 1 Gunicorn workers. Monitor and adjust. Run Gunicorn under systemd so it restarts automatically, and you have a robust production stack.