Deploying Django: Production Deployment with Gunicorn and Nginx

Production deployment transforms Django applications from development environments into reliable scalable systems serving real users through proper web servers, reverse proxies, and process management ensuring availability, security, and performance. Django's built-in development server suffices for local testing but lacks production features like multi-threading, process management, and security hardening making it unsuitable for real traffic. Gunicorn (Green Unicorn) serves as production WSGI server executing Django code handling multiple concurrent requests through worker processes managing application lifecycle. Nginx acts as reverse proxy sitting in front of Gunicorn serving static files, handling SSL termination, load balancing across workers, and protecting against common attacks providing first line of defense. Production deployment requires environment configuration separating development from production settings, static file collection serving CSS and JavaScript efficiently, database migrations ensuring schema updates, and process supervision maintaining application uptime. This comprehensive guide explores Django production deployment including understanding WSGI servers and reverse proxies, installing and configuring Gunicorn with optimal worker settings, setting up Nginx as reverse proxy and static file server, implementing SSL/HTTPS with Let's Encrypt certificates, configuring systemd for process management and automatic restarts, deploying to cloud platforms like AWS EC2, DigitalOcean, and Heroku, implementing zero-downtime deployments, monitoring application health and performance, and best practices for reliable production operations throughout application lifecycle from initial deployment through scaling to millions of users integrated with security measures and optimization techniques.
Gunicorn Configuration
Gunicorn implements WSGI specification enabling Python web applications to communicate with web servers through standard interface replacing Django's development server with production-grade capabilities. Worker processes handle concurrent requests with recommended count of (2 x CPU cores) + 1 balancing responsiveness against memory usage. Worker types include sync for CPU-bound tasks, gevent for I/O-bound workloads, and threads for mixed operations each offering different performance characteristics. Understanding Gunicorn configuration integrated with Django project structure enables optimal production performance maintaining fast response times under load.
# Install Gunicorn
pip install gunicorn
# Basic Gunicorn command
gunicorn myproject.wsgi:application
# Production Gunicorn with options
gunicorn myproject.wsgi:application \
--bind 0.0.0.0:8000 \
--workers 4 \
--threads 2 \
--timeout 60 \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
--log-level info
# Gunicorn configuration file
# gunicorn_config.py
import multiprocessing
# Server socket
bind = '0.0.0.0:8000'
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync' # or 'gevent', 'gthread'
worker_connections = 1000
threads = 2
timeout = 30
keepalive = 2
# Server mechanics
daemon = False # Let systemd manage daemonization
pidfile = '/var/run/gunicorn/gunicorn.pid'
user = 'www-data'
group = 'www-data'
tmp_upload_dir = None
# Logging
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'info'
access_log_format = '%({X-Forwarded-For}i)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
# Process naming
proc_name = 'myproject'
# Server hooks
def on_starting(server):
print("Gunicorn starting...")
def on_reload(server):
print("Gunicorn reloading...")
def when_ready(server):
print("Gunicorn ready")
def worker_int(worker):
print(f"Worker {worker.pid} received SIGINT")
# Run with config file
gunicorn -c gunicorn_config.py myproject.wsgi:application
# Worker class options:
# 1. Sync workers (default) - CPU-bound
workers = 4
worker_class = 'sync'
# 2. Gevent workers - I/O-bound (requires gevent)
# pip install gevent
workers = 4
worker_class = 'gevent'
worker_connections = 1000
# 3. Thread workers - Mixed workloads
workers = 2
worker_class = 'gthread'
threads = 4 # workers * threads = total threads
# 4. Uvicorn workers - ASGI (Django 3.0+)
# pip install uvicorn[standard]
workers = 4
worker_class = 'uvicorn.workers.UvicornWorker'
# Environment variables for Gunicorn
export DJANGO_SETTINGS_MODULE=myproject.settings.production
export PYTHONPATH=/path/to/project
gunicorn myproject.wsgi:application -c gunicorn_config.pyNginx Reverse Proxy Setup
Nginx serves as reverse proxy forwarding requests to Gunicorn while handling static files, SSL termination, gzip compression, and security headers providing robust frontend for Django applications. Reverse proxy configuration defines upstream Gunicorn servers, proxy headers for client information, and timeout settings for long-running requests. Static file serving through Nginx offloads Django from file delivery improving performance significantly for CSS, JavaScript, images, and media uploads. Understanding Nginx configuration integrated with static file management enables efficient production deployments.
# Install Nginx
sudo apt update
sudo apt install nginx
# Nginx configuration for Django
# /etc/nginx/sites-available/myproject
upstream django {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80;
server_name example.com www.example.com;
client_max_body_size 20M;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Static files
location /static/ {
alias /var/www/myproject/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Media files
location /media/ {
alias /var/www/myproject/media/;
expires 7d;
}
# Proxy to Django
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://django;
}
# Error pages
error_page 502 503 504 /502.html;
location = /502.html {
root /var/www/myproject/errorpages;
internal;
}
}
# Enable site
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl restart nginx
# HTTPS configuration with Let's Encrypt
# Install certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d example.com -d www.example.com
# Nginx HTTPS configuration (after certbot)
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL optimization
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# ... rest of configuration ...
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
# Load balancing across multiple Gunicorn instances
upstream django {
least_conn; # Load balancing method
server 127.0.0.1:8000 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8001 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8002 weight=1 max_fails=3 fail_timeout=30s;
}
# Rate limiting
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://django;
}
}
}
# Gzip compression
http {
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
}Process Management with systemd
Systemd manages Gunicorn processes ensuring automatic startup on boot, restart on crashes, and graceful shutdowns during updates maintaining application availability. Service units define how systemd starts, stops, and monitors processes with socket activation enabling zero-downtime deployments. Understanding systemd configuration enables reliable production operations ensuring applications remain available through server restarts and failures.
# Systemd service file for Gunicorn
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon for myproject
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
User=www-data
Group=www-data
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/myproject
Environment="DJANGO_SETTINGS_MODULE=myproject.settings.production"
Environment="PYTHONPATH=/var/www/myproject"
ExecStart=/var/www/myproject/venv/bin/gunicorn \
--config /var/www/myproject/gunicorn_config.py \
myproject.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
# Socket file for zero-downtime deploys
# /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=www-data
[Install]
WantedBy=sockets.target
# Enable and start services
sudo systemctl daemon-reload
sudo systemctl enable gunicorn.socket
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.service
sudo systemctl start gunicorn.service
# Service management commands
sudo systemctl status gunicorn
sudo systemctl restart gunicorn
sudo systemctl reload gunicorn # Graceful reload
sudo systemctl stop gunicorn
sudo systemctl start gunicorn
# View logs
sudo journalctl -u gunicorn.service -f
sudo journalctl -u gunicorn.service --since today
# Environment file
# /etc/systemd/system/gunicorn.service.d/env.conf
[Service]
EnvironmentFile=/var/www/myproject/.envProduction Deployment Checklist
Production deployment requires systematic preparation ensuring security, performance, and reliability through configuration verification, dependency management, and monitoring setup. Checklist items cover settings configuration, static file collection, database migrations, security measures, and backup procedures maintaining application quality from deployment through operations.
# Production Deployment Checklist
# 1. Django settings
# settings/production.py
DEBUG = False
ALLOWED_HOSTS = ['example.com', 'www.example.com']
SECRET_KEY = os.environ.get('SECRET_KEY')
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
# Static files
STATIC_ROOT = '/var/www/myproject/staticfiles'
MEDIA_ROOT = '/var/www/myproject/media'
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/myproject.log',
'maxBytes': 1024*1024*10, # 10 MB
'backupCount': 10,
'formatter': 'verbose',
},
},
'root': {
'handlers': ['file'],
'level': 'INFO',
},
}
# 2. Collect static files
python manage.py collectstatic --noinput
# 3. Run migrations
python manage.py migrate --noinput
# 4. Create superuser (if needed)
from django.contrib.auth import get_user_model
User = get_user_model()
if not User.objects.filter(username='admin').exists():
User.objects.create_superuser('admin', '[email protected]', 'secure_password')
# 5. Check deployment
python manage.py check --deploy
# 6. Test configuration
python manage.py test
# 7. Deployment script
#!/bin/bash
# deploy.sh
set -e # Exit on error
echo "Starting deployment..."
# Pull latest code
cd /var/www/myproject
git pull origin main
# Activate virtual environment
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Collect static files
python manage.py collectstatic --noinput
# Run migrations
python manage.py migrate --noinput
# Restart Gunicorn
sudo systemctl reload gunicorn
echo "Deployment complete!"
# 8. Health check endpoint
# views.py
from django.http import JsonResponse
from django.db import connection
def health_check(request):
try:
# Check database
with connection.cursor() as cursor:
cursor.execute("SELECT 1")
return JsonResponse({
'status': 'healthy',
'database': 'connected'
})
except Exception as e:
return JsonResponse({
'status': 'unhealthy',
'error': str(e)
}, status=500)| Component | Role | Alternative | Best For |
|---|---|---|---|
| Nginx | Reverse proxy, static files | Apache, Caddy | High performance, simple config |
| Gunicorn | WSGI server | uWSGI, Daphne | Python-focused, easy setup |
| PostgreSQL | Database | MySQL, MariaDB | Advanced features, reliability |
| systemd | Process manager | Supervisor, Docker | Linux systems, native integration |
Production Best Practices
- Automate deployments: Use scripts or CI/CD pipelines eliminating manual steps preventing errors
- Implement zero-downtime: Use systemd socket activation or blue-green deployments maintaining availability during updates
- Enable HTTPS everywhere: Use Let's Encrypt for free SSL certificates encrypting traffic following security practices
- Monitor application health: Implement health check endpoints tracking uptime, response times, and errors
- Configure logging: Set up centralized logging capturing errors, warnings, and security events
- Use environment variables: Store secrets outside code using .env files or secret management services
- Implement backups: Automate database and media backups testing restore procedures regularly
- Set resource limits: Configure Gunicorn workers, Nginx connections, and database connections preventing resource exhaustion
- Enable caching: Implement Redis caching and CDN for static files reducing server load
- Plan for scaling: Design architecture supporting horizontal scaling through load balancers and multiple servers
Conclusion
Production deployment transforms Django applications from development environments into reliable scalable systems through proper web server configuration, process management, and operational procedures ensuring availability and performance serving real users. Gunicorn replaces Django's development server providing production WSGI capabilities with worker processes handling concurrent requests configured through worker count, worker types including sync for CPU-bound tasks and gevent for I/O-bound workloads, and timeout settings preventing hung requests. Nginx serves as reverse proxy sitting in front of Gunicorn handling static file delivery offloading Django, SSL termination encrypting traffic with Let's Encrypt certificates, gzip compression reducing bandwidth, security headers protecting against attacks, and load balancing distributing requests across multiple Gunicorn instances. Systemd manages Gunicorn lifecycle ensuring automatic startup on boot, restart on crashes maintaining uptime, graceful reloads during updates preventing downtime, and logging through journalctl providing operational visibility. Production configuration requires DEBUG=False preventing information disclosure, ALLOWED_HOSTS restricting domain access, security settings enabling HTTPS redirects and secure cookies, static file collection with collectstatic, database migrations ensuring schema updates, and comprehensive logging capturing errors and security events. Deployment checklist covers settings verification ensuring production configuration, dependency installation with requirements.txt, static file collection serving assets efficiently, migration execution updating database schema, security hardening following Django check --deploy recommendations, health check endpoints monitoring application status, and automated deployment scripts preventing manual errors. Best practices include automating deployments through scripts or CI/CD pipelines, implementing zero-downtime deployments using socket activation or blue-green strategies, enabling HTTPS everywhere with SSL certificates, monitoring application health tracking uptime and errors, configuring centralized logging capturing events, using environment variables storing secrets securely, implementing automated backups with tested restore procedures, setting resource limits preventing exhaustion, enabling caching with Redis and CDN, and planning for horizontal scaling through load balancers. Production deployment integrates with Docker containerization, PostgreSQL database management, security hardening, and optimization techniques creating robust systems handling thousands of concurrent users maintaining fast response times and high availability throughout application lifecycle from initial deployment through enterprise-scale operations serving millions of requests daily.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


