Simon Westphahl

Running Django with Nginx and uWSGI

By Simon Westphahl on April 08, 2010

Since there is no pre-built Ubuntu package for uWSGI and the Nginx package is quite outdated, you have to build both from source. uWSGI requires at least a 0.7.x release of Nginx. First of all download and unpack the latest source of uWSGI and the source of Nginx to /usr/local/src/.

I created an isolated Python environment in /opt/example.com with virtualenv. The root of my Django project throughout this example is /opt/example.com/project/.

Getting started with uWSGI

The first step is to compile uWSGI (make) and copy the resulting binary to /usr/sbin/. Now what’s left is to setup uWSGI to run your Django application.

You have two choices to configure uWSGI, via an XML file or using Python. I prefer the Python way, but if you would rather use XML you are free to do so.

# wsgi_app.py
import sys
import os

sys.path.append(os.path.abspath(os.path.dirname(__file__)))
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'

import django.core.handlers.wsgi

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

Normally, I put this file in the root of my Django project so that it is under version control.

Running uWSGI

uWSGI brings its own process manager but the preferred way to run your apps with uWSGI would be to use a process manager like Upstart or daemontools. See “Running Processes” by Dustin Sallings for more alternatives.

This is a simple example script for use with Upstart:

# file: /etc/event.d/uwsgi_example
description "uWSGI server for example.com"

start on runlevel [2345]
stop on runlevel [!2345]

respawn
exec /usr/sbin/uwsgi \
--home /opt/example.com/ \
--socket /var/run/example.com.sock \
--chmod-socket \
--module wsgi_app \
--pythonpath /opt/example.com/project

The last line is necessary to locate the “wsgi_app” module if it doesn’t live on your Python path. Save this to the directory for your Upstart scripts (on Ubuntu 8.04 this is /etc/event.d, on more recent versions /etc/init). The name of this script is now used to start your uWSGI server:

$ start uwsgi_example

Compiling and Installing Nginx

Nginx doesn’t support dynamic loading of modules like Apache does, so you have to select those at compile time. I also made some choices regarding file locations but only the last line is required to make it play with uWSGI.

/usr/local/src/nginx:$ ./configure --conf-path=/etc/nginx/nginx.conf \
> --error-log-path=/var/log/nginx/error.log \
> --pid-path=/var/run/nginx.pid \
> --lock-path=/var/lock/nginx.lock \
> --http-log-path=/var/log/nginx/access.log \
> --http-client-body-temp-path=/var/lib/nginx/body \
> --http-proxy-temp-path=/var/lib/nginx/proxy \
> --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
> --with-http_stub_status_module \
> --with-http_flv_module \
> --with-http_ssl_module \
> --sbin-path=/usr/sbin \
> --add-module=../uwsgi/nginx/

Have a look at the Nginx wiki which lists all available compile-time options.

Now we are ready to compile and install:

/usr/local/src/nginx:$ make && checkinstall

I also had to create some directories by hand:

$ mkdir /var/run/nginx \
/var/log/nginx \
/var/lib/nginx \
/var/lib/nginx/body \
/var/lib/nginx/proxy \
/var/lib/nginx/fastcgi

You can create a simple Upstart script or use the classical Init way to run Nginx. If you want to use Init you should follow the steps provided by the Nginx wiki.

The location part for your Django application in your nginx.conf should look something like:

location / {
    uwsgi_pass unix:///var/run/example.com.sock;
    include uwsgi_params;
}

The uwsgi_params file came with the source of uWSGI and should be in /usr/local/src/uwsgi/nginx. It must be copied over to /etc/nginx or wherever your Nginx configuration lives.

Closing Words

In my opinion uWSGI offers a simple and uncomplicated way to run your WSGI application. It seems very fast and stable while keeping memory consumption very low.

I haven’t done any benchmarks by myself but Nicholas Piël wrote a very good article about the performance of Python WSGI servers.

If you have any questions, feel free to comment.