Django Web Servers
When you develop a Django application locally, you typically use the built-in development server with the python manage.py runserver
command. However, this server is not suitable for production environments. In this tutorial, we'll explore different web servers you can use to deploy your Django applications in production.
Introduction to Django Web Servers
The Django development server is convenient for local development, but it has several limitations:
- It's single-threaded and can only handle one request at a time
- It hasn't been security-audited
- It doesn't scale well under load
- It's not optimized for performance
For production environments, you need a proper web server setup that can:
- Handle multiple concurrent connections
- Provide security features
- Efficiently serve static and media files
- Ensure high availability and performance
Common Django Web Server Architectures
WSGI Servers
WSGI (Web Server Gateway Interface) is a specification that describes how a web server communicates with web applications. Django applications are WSGI-compatible, which means they can work with any WSGI server.
Popular WSGI servers for Django include:
- Gunicorn (Green Unicorn)
- uWSGI
- mod_wsgi (for Apache)
Web Server + WSGI Server Architecture
The most common production architecture follows this pattern:
Client Request → Web Server (Nginx/Apache) → WSGI Server (Gunicorn/uWSGI) → Django Application
In this setup:
- The web server handles static files, SSL termination, and load balancing
- The WSGI server runs your Django application and processes Python code
Setting Up Gunicorn with Django
Gunicorn is a popular WSGI HTTP server for Python that's lightweight and easy to set up.
Step 1: Install Gunicorn
pip install gunicorn
Add it to your requirements.txt
or Pipfile
:
Django==4.2.0
gunicorn==21.2.0
# other dependencies
Step 2: Run Your Django Application with Gunicorn
Instead of using runserver
, you can now use Gunicorn:
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
Where:
myproject.wsgi:application
is the path to your WSGI application--bind 0.0.0.0:8000
tells Gunicorn to listen on all interfaces on port 8000
Step 3: Configure Gunicorn for Production
For production, you'd typically use a more robust configuration. Create a file named gunicorn_config.py
:
# gunicorn_config.py
bind = "0.0.0.0:8000"
workers = 3 # Recommended formula: 2 * number_of_cores + 1
worker_class = "gevent" # or "sync", "eventlet", etc.
timeout = 60
keepalive = 5
errorlog = "/var/log/gunicorn/error.log"
accesslog = "/var/log/gunicorn/access.log"
loglevel = "info"
Then run Gunicorn with this config:
gunicorn -c gunicorn_config.py myproject.wsgi:application
Setting Up Nginx with Gunicorn
While Gunicorn can serve your Django application, it's best practice to put a dedicated web server like Nginx in front of it.
Step 1: Install Nginx
# On Ubuntu/Debian
sudo apt-get install nginx
# On CentOS/RHEL
sudo yum install nginx
# On macOS
brew install nginx
Step 2: Configure Nginx
Create a configuration file for your Django project:
# /etc/nginx/sites-available/myproject.conf
server {
listen 80;
server_name example.com www.example.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /path/to/your/project;
}
location /media/ {
root /path/to/your/project;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
}
}
Enable the configuration:
# On Ubuntu/Debian
sudo ln -s /etc/nginx/sites-available/myproject.conf /etc/nginx/sites-enabled/
sudo nginx -t # Test the configuration
sudo systemctl restart nginx
Now Nginx will:
- Serve static and media files directly (more efficient)
- Forward other requests to Gunicorn
- Handle multiple connections simultaneously
Using Apache with mod_wsgi
Apache is another popular web server that can run Django applications using the mod_wsgi module.
Step 1: Install Apache and mod_wsgi
# On Ubuntu/Debian
sudo apt-get install apache2 libapache2-mod-wsgi-py3
# On CentOS/RHEL
sudo yum install httpd mod_wsgi
Step 2: Configure Apache
Create a virtual host configuration:
# /etc/apache2/sites-available/myproject.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /path/to/your/project
<Directory /path/to/your/project>
Require all granted
</Directory>
Alias /static/ /path/to/your/project/static/
<Directory /path/to/your/project/static>
Require all granted
</Directory>
Alias /media/ /path/to/your/project/media/
<Directory /path/to/your/project/media>
Require all granted
</Directory>
WSGIDaemonProcess myproject python-path=/path/to/your/project:/path/to/your/virtualenv/lib/python3.x/site-packages
WSGIProcessGroup myproject
WSGIScriptAlias / /path/to/your/project/myproject/wsgi.py
ErrorLog ${APACHE_LOG_DIR}/myproject-error.log
CustomLog ${APACHE_LOG_DIR}/myproject-access.log combined
</VirtualHost>
Enable the configuration:
# On Ubuntu/Debian
sudo a2ensite myproject.conf
sudo systemctl restart apache2
Running Django with ASGI (Asynchronous Server Gateway Interface)
If your Django application uses asynchronous features (introduced in Django 3.0+), you might want to use an ASGI server like Daphne or Uvicorn.
Using Uvicorn
- Install Uvicorn:
pip install uvicorn
- Run your Django application with Uvicorn:
uvicorn myproject.asgi:application --host 0.0.0.0 --port 8000
Process Management with Supervisor
To ensure your WSGI server stays running, you should use a process manager like Supervisor.
Step 1: Install Supervisor
sudo apt-get install supervisor
Step 2: Configure Supervisor
Create a configuration file for your Django application:
# /etc/supervisor/conf.d/myproject.conf
[program:myproject]
command=/path/to/virtualenv/bin/gunicorn -c /path/to/gunicorn_config.py myproject.wsgi:application
directory=/path/to/your/project
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/supervisor/myproject.log
Start the Supervisor process:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myproject
Production Checklist and Best Practices
When deploying Django with a web server, follow these best practices:
- Set DEBUG = False in your settings.py file
- Set appropriate ALLOWED_HOSTS
- Configure static files properly:
# settings.py for production
DEBUG = False
ALLOWED_HOSTS = ['example.com', 'www.example.com']
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
- Run Django's check deployment command:
python manage.py check --deploy
- Use HTTPS (SSL/TLS) for all production sites
- Set appropriate timeouts on your web servers
- Monitor your servers for issues and performance
Real-World Django Deployment Example
Let's put everything together in a complete example for deploying a Django e-commerce site:
- Set up the Django project for production:
# settings.py (production settings)
DEBUG = False
ALLOWED_HOSTS = ['shop.example.com']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myshopdb',
'USER': 'myshopuser',
'PASSWORD': 'super_secret_password',
'HOST': 'db.example.com',
'PORT': '5432',
}
}
# Static and media files
STATIC_URL = '/static/'
STATIC_ROOT = '/var/www/myshop/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = '/var/www/myshop/media/'
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
- Set up Gunicorn configuration:
# gunicorn_config.py
bind = "127.0.0.1:8000"
workers = 4
worker_class = "gevent"
timeout = 60
keepalive = 5
max_requests = 1000
max_requests_jitter = 50
errorlog = "/var/log/gunicorn/myshop-error.log"
accesslog = "/var/log/gunicorn/myshop-access.log"
loglevel = "info"
- Set up Nginx configuration:
# /etc/nginx/sites-available/myshop.conf
server {
listen 80;
server_name shop.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name shop.example.com;
ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_session_cache shared:SSL:10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
alias /var/www/myshop/static/;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
location /media/ {
alias /var/www/myshop/media/;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://127.0.0.1:8000;
}
# Custom error pages
error_page 404 /404.html;
error_page 500 502 503 504 /500.html;
}
- Set up Supervisor to manage Gunicorn:
# /etc/supervisor/conf.d/myshop.conf
[program:myshop]
command=/var/www/venv/bin/gunicorn -c /var/www/myshop/gunicorn_config.py myshop.wsgi:application
directory=/var/www/myshop
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/supervisor/myshop.log
environment=DJANGO_SETTINGS_MODULE="myshop.settings.production"
- Deployment commands:
# Collect static files
python manage.py collectstatic --no-input
# Apply migrations
python manage.py migrate
# Start services
sudo supervisorctl restart myshop
sudo systemctl restart nginx
Summary
In this tutorial, we've covered:
- Why the Django development server isn't suitable for production
- Common web server architectures for Django deployment
- Setting up Gunicorn as a WSGI server
- Configuring Nginx and Apache as web servers
- Managing processes with Supervisor
- Best practices for Django deployment
- A real-world example of a complete Django deployment
By using a proper web server setup, you ensure your Django application can handle real-world traffic, remain secure, and provide optimal performance.
Additional Resources
- Official Django Deployment Checklist
- Gunicorn Documentation
- Nginx Documentation
- Apache mod_wsgi Documentation
- Supervisor Documentation
Practice Exercises
- Set up a local development environment with Gunicorn running your Django application
- Install Nginx and configure it to serve static files for your Django project
- Create a Supervisor configuration file for your Django app
- Configure SSL for your local development environment using self-signed certificates
- Benchmark the performance of different WSGI servers (Gunicorn, uWSGI) with tools like Apache Bench or wrk
Happy deploying!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)