Development Long read Deployment
Ship Django with uWSGI

Ship Django with uWSGI

Complete production deployment steps.

2 March 2026 21 min read
Share
X in

Introduction

Moving a Django app from development to production requires coordinating multiple components: WSGI entry point, uWSGI ini, systemd service, Nginx, static files, and environment variables.

This guide provides an end-to-end deployment flow: from server prep to first deploy and update strategy. Optimized for teams choosing uWSGI over Gunicorn.

Examples target Django 5.x, Python 3.12, and Debian/Ubuntu servers.

Server Preparation

Install Python 3.12, nginx, uwsgi-plugin-python3. Create a deploy user; do not run uWSGI as root. Prepare /var/www/myproject and /run/uwsgi.

Configure PostgreSQL or your database. Set DJANGO_SECRET_KEY, DATABASE_URL, and ALLOWED_HOSTS in .env; never commit to repo.

sudo apt install python3.12-venv nginx uwsgi uwsgi-plugin-python3
sudo useradd -m -s /bin/bash deploy
sudo mkdir -p /run/uwsgi /var/log/uwsgi
sudo chown deploy:www-data /run/uwsgi

Django WSGI and Environment

myproject/wsgi.py is Django's default WSGI entry point. DJANGO_SETTINGS_MODULE=myproject.settings loads production settings.

settings/production.py has DEBUG=False, secure cookie settings, and logging config. Read .env with django-environ.

  • DEBUG=False zorunlu
  • ALLOWED_HOSTS domain listesi
  • STATIC_ROOT collectstatic hedefi
  • SECURE_PROXY_SSL_HEADER nginx için
# myproject/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')
application = get_wsgi_application()

uWSGI ini and systemd

ini defines chdir, virtualenv, module, and socket. env = DJANGO_SETTINGS_MODULE=myproject.settings.production passes the environment variable.

systemd unit Type=notify or simple; Restart=always for automatic recovery.

[Unit]
Description=uWSGI Django myproject
After=network.target

[Service]
User=deploy
Group=www-data
ExecStart=/var/www/myproject/venv/bin/uwsgi --ini /etc/uwsgi/apps-enabled/myproject.ini
Restart=always
KillSignal=SIGQUIT

[Install]
WantedBy=multi-user.target

Static Files and Migrate

python manage.py collectstatic --noinput gathers files to STATIC_ROOT. Nginx /static/ location aliases to this directory.

Run python manage.py migrate --noinput in deploy script. Complete migrations before app starts; order matters.

#!/bin/bash
set -e
cd /var/www/myproject
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate --noinput
python manage.py collectstatic --noinput
touch reload.touch

Zero-Downtime Deploy

touch-reload = /var/www/myproject/reload.touch triggers graceful reload on code update. chain-reload renews workers one by one.

Blue-green deploy can use two sockets and Nginx upstream switch; touch-reload suffices for most projects.

[uwsgi]
touch-reload = /var/www/myproject/reload.touch
chain-reload = true
lazy-apps = true
need-app = true
Deploy script'inizde migrate → collectstatic → touch-reload sırasını asla bozmayın.

Security and Hardening

Harden with uid/gid drop, harakiri, limit-as, and disable-logging=false. Open only 80/443 on firewall; restrict stats port to internal network.

Django security middleware, CSRF, HSTS, and SECURE_SSL_REDIRECT must be enabled in production.

  1. DEBUG kapalı
  2. Güçlü SECRET_KEY
  3. HTTPS zorunlu
  4. Veritabanı şifreleri .env'de
  5. Düzenli güvenlik güncellemeleri

Conclusion

The Django + uWSGI + systemd + Nginx stack is a proven production architecture. Reliable operations come from automated deploy scripts, touch-reload, and log monitoring.

After initial setup consider Emperor mode or containerization; but do not add complexity before the basic flow is solid.