Skip to main content

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:

  1. Gunicorn (Green Unicorn)
  2. uWSGI
  3. 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

bash
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:

bash
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:

python
# 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:

bash
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

bash
# 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:

nginx
# /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:

bash
# 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

bash
# 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:

apache
# /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:

bash
# 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

  1. Install Uvicorn:
bash
pip install uvicorn
  1. Run your Django application with Uvicorn:
bash
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

bash
sudo apt-get install supervisor

Step 2: Configure Supervisor

Create a configuration file for your Django application:

ini
# /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:

bash
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:

  1. Set DEBUG = False in your settings.py file
  2. Set appropriate ALLOWED_HOSTS
  3. Configure static files properly:
python
# 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')
  1. Run Django's check deployment command:
bash
python manage.py check --deploy
  1. Use HTTPS (SSL/TLS) for all production sites
  2. Set appropriate timeouts on your web servers
  3. 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:

  1. Set up the Django project for production:
python
# 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'
  1. Set up Gunicorn configuration:
python
# 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"
  1. Set up Nginx configuration:
nginx
# /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;
}
  1. Set up Supervisor to manage Gunicorn:
ini
# /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"
  1. Deployment commands:
bash
# 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:

  1. Why the Django development server isn't suitable for production
  2. Common web server architectures for Django deployment
  3. Setting up Gunicorn as a WSGI server
  4. Configuring Nginx and Apache as web servers
  5. Managing processes with Supervisor
  6. Best practices for Django deployment
  7. 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

Practice Exercises

  1. Set up a local development environment with Gunicorn running your Django application
  2. Install Nginx and configure it to serve static files for your Django project
  3. Create a Supervisor configuration file for your Django app
  4. Configure SSL for your local development environment using self-signed certificates
  5. 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! :)