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.