Brandon Konkle
Brandon Konkle

Principal Engineer, type system nerd, Rust enthusiast, supporter of social justice, loving husband & father, avid comic & manga reader, 日本語を勉強してる。

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.



Django on uWSGI and Nginx

I recently moved Pegasus News from Perlbal, Lighttpd, and Apache to Nginx and uWSGI. We balance the traffic between 3 physical servers, and the systems were struggling under the load even after weeks of Apache conf tweaking. We began having issues with excessively slow page loads, request timeouts, and intermittent errors with OGR transformations.

I decided to move us to a lighter application server so that we could get the most out of our system resources, and after a lot of research and testing I chose uWSGI. While I was at it, I decided to replace Perlbal and Lighttpd with Nginx because of its great configuration syntax and excellent performance. I also upgraded us to Ubuntu 10.04 and Postgres 8.4.

The result was a resounding success! Memory usage and CPU load on each of the web nodes dropped dramatically. Swap usage dropped to almost nothing. The overall responsiveness of the site improved noticeably, and the timeout errors and OGR failures disappeared entirely.

If you'd like to give this stack a try, read on for an overview of the setup on Ubuntu 10.04. I'm using the standard Ubuntu source package for Nginx, but modifying it slightly and them installing it with dpkg-buildpackage.

This post is just about the setup relevant to Nginx and uWSGI. If you need a more complete server setup guide, try my Provisioning a new server post.


Before we build Nginx, uWSGI needs to be compiled so that its module can be included in the Nginx build.

$ sudo apt-get install build-essential python-dev libxml2-dev
$ wget
$ tar -xzf uwsgi-
$ cd uwsgi-
$ make -f Makefile.Py26

Copy the executable to the local sbin directory.

$ sudo cp uwsgi /usr/local/sbin

Also, copy the default uwsgi settings to the /etc/nginx directory.

$ sudo mkdir /etc/nginx
$ sudo cp nginx/uwsgi_params /etc/nginx
$ cd ..


We need to slightly modify the nginx package from Ubuntu to add uWSGI (and, optionally, SSL).

$ sudo apt-get install libssl-dev
$ sudo apt-get build-dep nginx
$ apt-get source nginx
$ cd nginx-0.7*
$ emacs debian/rules

Add the following lines to the end of the configure options. Make sure to include backslashes so that all the options are interpreted as being on one line. If you don't need SSL, ignore that line.


Build, install, and hold the packages.

$ dpkg-buildpackage
$ cd ..
$ sudo dpkg -i nginx*.deb
$ echo "nginx hold" | sudo dpkg --set-selections
$ echo "nginx-dbg hold" | sudo dpkg --set-selections

Starting with nginx-0.8.41, you can add something like --http-uwsgi-temp-path=/var/lib/nginx/uwsgi to the debian/rules so that the temp files are kept in the right place. Until then (probably a later version of Ubuntu), you'll need to create a /usr/local/nginx/uwsgi_temp folder to use for the temp files.

$ sudo mkdir -p /usr/local/nginx/uwsgi_temp


To manage the uWSGI processes, I use Supervisor. In Ubuntu 10.04, you can simply install it with apt-get.

$ sudo apt-get install supervisor



To configure uWSGI, I use command-line options in my Supervisor config. The example below is similar to what I use in production for Pegasus, but you'll want to take a look at the uWSGI docs and tweak for your situation.

  --home /home/myuser/.virtualenvs/myapp/
  --module myapp.deploy.wsgi
  --pythonpath /sites/
  --processes 5
  --harakiri 120
  --max-requests 5000

The module I specify in the --module option simply contains:

import django.core.handlers.wsgi

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

The IP address in the --socket option is the server's IP address on the interface reserved for local network traffic. On Rackspace, eth1 is your local interface. Use the ifconfig command to find your IP address on the local interface. If you're just using uWSGI on localhost, you can use something like --socket /sites/ instead to avoid the overhead of the full TCP stack.

My value for --harakiri is rather high, and my value for --max-requests is rather low. You may not even need either of these options, but I'm using them to solve some problems specific to Pegasus.


For the site-specific Nginx config, I'm using something like this:

upstream myapp {

server {
    listen 80;
    listen 443;

    access_log /sites/;
    error_log /sites/;

    root /sites/;

    ssl_certificate /sites/;
    ssl_certificate_key /sites/;

    location / {
        # This checks for a file called simply "downtime" in the public
        # directory, and puts up the downtime page if it exists.
        if (-f /sites/ {
            return 503;

        uwsgi_pass myapp;
        include uwsgi_params;

    location /media {
        # This makes static media available at the /media/ url.  The
        # media will continue to be available during site downtime,
        # allowing you to use styles and images in your maintenance page.
        alias /sites/;

    error_page 502 503 504 @maintenance;
    location @maintenance {
        # In this example, there's a directory in the site media files
        # called "downtime" that contains a "maintenance.html" file and
        # any styles and images needed for the maintenance page.
        root /sites/;
        rewrite ^(.*)$ /maintenance.html break;    

If you're just using uWSGI on localhost, then skip the upstream section and use something like uwsgi_pass unix:///sites/; in the root location definition instead.

Firing it up

Restart Supervisor with sudo /etc/init.d/supervisor restart, and it should reload the config and bring the uWSGI processes up. Restart Nginx with sudo /etc/init.d/nginx restart, and you should now be able to reach your site. If not, take a look at the logs and start troubleshooting.

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.

View Comments