Deploy Django in production using apache, nginx and mod-wsgi

In this post I will show how to deploy a Django applications using Apache with mod-wsgi and nginx.
nginx is used to serve static content as well as proxy requests to Apache. This means faster page loads for users, and lower server loads.


Install Apache2, mod-wsgi and nginx

Install all required packages from repositories:

apt-get install apache2 apache2.2-common apache2-mpm-prefork \
libapache2-mod-wsgi apache2-utils libexpat1 ssl-cert nginx

Install Django

Same as above:

sudo apt-get install python-django

Create Django site folders

sudo mkdir -p /home/sites/domain.tld/{public_html,logs,static}
sudo chown -R www-data:www-data /home/sites/

public_html directory will be used for publicly accessible files served by nginx
log directory will store the Apache and Nginx logs for the domain
static directory will store the static content

Create a Django project called proj using django-admin tool:

cd /home/sites/domain.tld/public_html/
django-admin startproject proj

Alter file and set the MEDIA_URL and MEDIA_ROOT settings appropriately in project settings file /home/sites/domain.tld/public_html/proj/

MEDIA_ROOT = '/home/sites/domain.tld/static/'
MEDIA_URL = 'http://www.domain.tld/static/'

This variables allow to specify the location of static files server by nginx

Configure Apache

To accomplish this, setup a virtual host that tells Apache where is located WSGI file. Open /etc/apache2/sites-available/domain.tld and fill it with following content:

ServerName domain.tld
ServerAlias www.domain.tld
WSGIScriptAlias / /home/sites/domain.tld/public_html/proj.wsgi

Any request to Apache will execute the WSGI Script from

To tell mod_wsgi how to handle the requests, create a WSGI file located at /home/sites/domain.tld/public_html/proj.wsgi with following contents:

import os
import sys

os.environ['DJANGO_SETTINGS_MODULE'] = 'proj.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Once the request is passed to mod_wsgi to invoke the above Django code.

Configure nginx

Make small changes to nginx configuration file /etc/nginx/nginx.conf:

worker_processes  4;
tcp_nopush on;

This tells nginx to use four worker processes and to send the HTTP response headers in one packet

Create an Nginx virtual host for the domain, by opening in editor /etc/nginx/sites-available/domain.tld and filling it with content:

server {
listen   ip.address:80;
server_name  www.domain.tld;

access_log /home/sites/domain.tld/logs/access.log;
error_log /home/sites/domain.tld/logs/error.log;

location / {
include /etc/nginx/proxy.conf;

location /static/ {
root /home/sites/domain.tld/static/;
expires 1d;

The above config tells nginx to pass requests for dynamic content to Apache and to serve the static content if request contains /static location.

Create a /etc/nginx/proxy.conf file with the following contents:

proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;

The file tells to nginx how to serve the proxy requests

Apache should listen on local address. Open /etc/apache2/ports.conf and alter the following lines:


Disable KeepAlive in /etc/apache2/apache2.conf:

KeepAlive Off

Because, nginx doesn't yet support the persistent connections.

Enable site

Create a symlinks to the desired virtual hosts. For Apache it could be done with command:

a2ensite domain.tld

For nginx, it is done manually, by symlinking it from sites-available to sites-enabled:

ln -s /etc/nginx/sites-available/domain.tld /etc/nginx/sites-enabled/domain.tld

Run site

Remove the symlinks for default sites and restart the webservers:

rm /etc/nginx/{sites-available,sites-enabled}/default
rm /etc/apache2/{sites-available,sites-enabled}/000-default

Restart the webservers to make the updates take effect:

/etc/init.d/apache2 restart
/etc/init.d/nginx restart

Navigating to your site address will show the default 'It worked'.