As a Python or Django developer, various WSGI and webservers are available to deploy an application. A very fast, lightweight and relatively easy-to-install solution is the combination of Nginx, the high-performance HTTP server, and uWSGI (see benchmark of python WSGI servers). Furthermore, virtualenv and Fabric are invaluable tools to handle Python dependencies and to automate deployments.

This tutorial describes how to setup a Django 1.3, Nginx and uWSGI stack. My OS at the time of this writing was Ubuntu 10.04 LTS.

Installing all requirements

First install all the handy python tools, if you don't have them already (maybe in your virtualenv):

apt-get install python-virtualenv python-pip fabric

Nginx is not yet in the standard Ubuntu packages, therefore one has to add an additional apt repository:

apt-get install python-software-properties
add-apt-repository ppa:nginx/development
apt-get update
apt-get install nginx

To install uWSGI, activate your virtualenv and install it with pip:

source /path/to/your/virtualenv/bin/activate
pip install uwsgi

Configure uWSGI

First you need a WSGI-script for your django project. Put the following file in your django project folder (same folder where settings.py lives) and name it wsgi.py:

import os
import sys
import site

site.addsitedir('/path/to/your/virtualenv/lib/python2.6/site-packages')

os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Then we need an init script for the uWSGI daemon, I used upstart, because I am lazy. The following file should be in /etc/init/uwsgi.conf, for a description of the uWSGI parameters see uWSGI documentation:

# file: /etc/init/uwsgi.conf
description "uWSGI starter"

start on (local-filesystems and runlevel [2345])
stop on runlevel [016]

respawn

# home - is the path to our virtualenv directory
# pythonpath - the path to our django application
# module - the wsgi handler python script

exec /path/to/your/virtualenv/bin/uwsgi \
--uid your-user \
--home /path/to/your/virtualenv \
--pythonpath /path/to/your/django/project \
--socket /tmp/uwsgi.sock \
--chmod-socket \
--module wsgi \
--logdate \
--optimize 2 \
--processes 2 \
--master \
--logto /path/to/your/log/uwsgi.log

Be careful to specify the right user (must have all necessary permissions, also for the database) - and no, root is not a good idea.

Configure Nginx

Put the Nginx configuration file for your domain in /etc/nginx/sites-available/yourdomain.com:

# file: /etc/nginx/sites-available/yourdomain.com
# nginx configuration for yourdomain.com

server {
        server_name www.yourdomain.com;
        rewrite ^(.*) http://yourdomain.com$1 permanent;
}

server {
        server_name yourdomain.com;
        access_log /path/to/your/log/access.log;
        error_log /path/to/your/log/error.log;

        location / {
                uwsgi_pass unix:/tmp/uwsgi.sock;
                include /etc/nginx/uwsgi_params;
        }

        location /static {
                root /root/path/of/your/static/files;
        }
        location /media {
                root /root/path/of/your/media/files;
        }
}

Then activate your domain:

ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/yourdomain.com

Nginx already ships with an init script, so we don't have to write another one.

Starting and Restarting the Server

To start the server type:

start uwsgi
/etc/init.d/nginx start

Or if you want to restart it:

restart uwsgi
/etc/init.d/nginx restart

And don't forget to set your domain in Django Admin -> Sites!

Deployment with Fabric

Put the following file into /path/to/your/django/project/fabfile.py:

""" Deployment of your django project.
"""

from fabric.api import *

env.hosts = ['yourdomain.com']
env.user = "your-user"

def update_django_project():
    """ Updates the remote django project.
    """
    with cd('/path/to/your/django/project'):
        run('git pull')
        with prefix('source /path/to/your/virtualenv/bin/activate'):
            run('pip install -r your_pip_requirement_file.txt')
            run('python manage.py syncdb')
            run('python manage.py migrate') # if you use south
            run('python manage.py collectstatic --noinput')

def restart_webserver():
    """ Restarts remote nginx and uwsgi.
    """
    sudo("service uwsgi restart")
    sudo("/etc/init.d/nginx restart")

def deploy():
    """ Deploy Django Project.
    """
    update_django_project()
    restart_webserver()

Then you can deploy your project with the command:

fab deploy

Further Resources

The following links helped me to get the system running:

Recent entries