Deploy Django With NGINX Unit

Run Django on NGINX Unit, the application server configured through a JSON API: configure a Python application with a virtualenv, route static files, set processes, add TLS, and check the project's maintenance status before adopting it.

Problem statement

NGINX Unit is an application server that runs Python (WSGI and ASGI), serves static files, terminates TLS, and is configured live through a JSON API instead of config files and restarts. For Django it can replace the Gunicorn plus Nginx pair with a single process, which appeals to teams that want fewer moving parts or containers with one server inside. Before choosing it for a new project, check the project's current maintenance status on its official repository: its development activity changed in 2025, and an unmaintained app server is a risk for security updates.

Django on NGINX UnitControl APIJSON configListener:80 / :443Routesstatic or appPython appvenv, processesUnit serves static files and runs Django from one process, configured through its API.
  1. Check the current maintenance status of NGINX Unit; for new long-lived projects, Gunicorn or Granian behind Nginx may be the safer default.
  2. Install Unit and its Python module for your Python version (or use the official Docker image).
  3. Define an application with type: python, path, home (venv), and module: config.wsgi (or config.asgi).
  4. Add routes: /static/* served from STATIC_ROOT with share, everything else passed to the app.
  5. Upload the configuration with curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config.
  6. Add certificates through the API for TLS, and tune processes.

Step-by-step solution

1. Install Unit

Unit is distributed as OS packages and Docker images with language modules, for example unit-python3.12. The Python module must match the Python version your virtualenv uses. Check the official installation docs for current package names and repositories.

2. Write the configuration

{
  "listeners": {
    "*:80": { "pass": "routes" }
  },
  "routes": [
    {
      "match": { "uri": "/static/*" },
      "action": { "share": "/srv/app/staticfiles$uri_without_static" }
    },
    {
      "action": { "pass": "applications/django" }
    }
  ],
  "applications": {
    "django": {
      "type": "python 3.12",
      "path": "/srv/app",
      "home": "/srv/app/.venv",
      "module": "config.wsgi",
      "callable": "application",
      "user": "deploy",
      "group": "www-data",
      "processes": { "max": 6, "spare": 2, "idle_timeout": 30 },
      "environment": { "DJANGO_SETTINGS_MODULE": "config.settings.production" }
    }
  }
}

Unit's share maps a URI to a file path; the simplest pattern is to make STATIC_ROOT mirror the URL (/srv/app/public/static/... with "share": "/srv/app/public$uri"). Use whichever variable syntax your Unit version documents.

Unit configuration objectslisteners*:80, *:443 with certificateroutesmatch /static/* firstshare actionserve files from STATIC_ROOTpass to applicationeverything elsepython applicationvenv home, module, processesOne JSON document replaces the Nginx site file and the Gunicorn unitdjango-deployment.com
Keep the JSON in Git and apply it from your deploy script.

3. Apply it through the control API

sudo curl -X PUT --data-binary @unit.json \
  --unix-socket /var/run/control.unit.sock http://localhost/config
sudo curl --unix-socket /var/run/control.unit.sock http://localhost/config/applications

Changes apply without dropping connections. Restart the app after deploying new code:

sudo curl -X GET --unix-socket /var/run/control.unit.sock http://localhost/control/applications/django/restart

4. TLS

Upload a certificate bundle (certificate chain plus key in one PEM file) and reference it from a listener:

cat fullchain.pem privkey.pem > bundle.pem
sudo curl -X PUT --data-binary @bundle.pem --unix-socket /var/run/control.unit.sock http://localhost/certificates/example
"listeners": { "*:443": { "pass": "routes", "tls": { "certificate": "example" } } }

Renewal needs a hook that re-uploads the bundle; this is more manual than Certbot with Nginx. Unit terminates TLS itself, so Django sees HTTPS requests directly through Unit's forwarded settings; configure SECURE_PROXY_SSL_HEADER only if another proxy sits in front.

5. Processes and limits

processes.max caps worker processes; spare keeps idle ones ready; idle_timeout stops extras. Size by memory as with Gunicorn workers. Set limits.timeout on the application to bound request duration.

should you adopt Unit?What matters most for this deployment?existing Unit setupKeep, watch statusplan a migration pathnew long-lived projectGunicorn or Granianbehind Nginx or Caddyone-process containersGranian or Unitcheck maintenance firstMaintenance status matters more than features for an app serverdjango-deployment.com
An app server sits in every request path; it needs security fixes for years.

Verification check: curl -I http://example.com/ returns Django's response, curl -I http://example.com/static/admin/css/base.css returns 200 from Unit, and curl --unix-socket /var/run/control.unit.sock http://localhost/status shows the application processes.

Explanation

NGINX Unit combines a router, static file server, TLS terminator, and language runtimes in one daemon, with configuration held in memory and changed through a REST API on a local Unix socket. For Django, the Python module embeds the interpreter and calls the WSGI or ASGI callable, managing processes per the configuration. The design removes the separate reverse proxy and the reload choreography of classic setups. The trade-off is ecosystem size and, at present, uncertainty about long-term maintenance, which is why mainstream setups remain the conservative choice.

Edge cases / notes

  • ASGI apps use "module": "config.asgi" and Unit detects the interface; WebSockets are supported.
  • Python version. Every Python upgrade needs the matching Unit language module.
  • Logs go to Unit's log file; configure access logs explicitly.
  • Migration away is straightforward: the same WSGI module runs under Gunicorn.

Next steps

FAQ

Can NGINX Unit replace Gunicorn and Nginx for Django?

Technically yes: it runs Django, serves static files, and terminates TLS. Check its maintenance status before adopting it.

How do I reload Django code in Unit?

Call the application restart endpoint on the control API after deploying new code.

Does Unit support ASGI and WebSockets?

Yes. Point the application module at config.asgi.

Is NGINX Unit the same as Nginx?

No. It is a separate project from the same organisation, with a different architecture and configuration model.

Keep going