How to Set Up Django with Nginx, uWSGI & systemd on Debian/Ubuntu

Topics: Web Servers, Python

I’ve served Django over all sorts of different servers, from Apache with mod_python to Nginx with Gunicorn. The current incarnation of my publishing system1 runs atop an Nginx server which passes requests for dynamic pages to uWSGI. I’ve found this setup to be the fastest of the various options out there for serving Django apps, particularly when pared with a nice, fast, cheap VPS instance.

I am apparently not alone in thinking uWSGI is fast. Some people have even tested uWSGI and proved as much. Honestly though speed is not what got me using uWSGI. I switched because just plays so much nicer with systemd than Gunicorn. Also, something about the Gunicorn project always rubbed me the wrong way, but that’s just me.

Anyway, my goal was to have a server running that’s managed by the system. In my case that means Debian 8 with systemd. I set things up so that a uWSGI “emperor” instance starts up with systemd and then automatically picks up any “vassals” residing a directory2. That way the server will automatically restart should the system need to reboot.

The first step in this dance is to install uWSGI, which is a Python application (for more background on how uWSGI works and what the various parts are, check out this tutorial). We could install uwsgi through the Debian repos with apt-get, but that version is pretty ancient, so I install uWSGI with pip.

pip install uwsgi

Now we need a systemd service file so that we can let systemd manage things for us. Here’s what I use. Note that the path is the standard Debian install location, your system may vary (though I believe Ubuntu is the same):

[Unit]
Description=uWSGI Emperor
After=syslog.target

[Service]
ExecStart=/usr/local/bin/uwsgi --ini /etc/uwsgi/emperor.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Save that to /lib/systemd/system/uwsgi.service

Then enable it and try starting it:

sudo systemctl enable uwsgi.service
sudo systemctl start uwsgi

This should cause an error like so…

Job for uwsgi.service failed. See 'systemctl status uwsgi.service' and 'journalctl -xn' for details.

If you look at the journal you’ll see that the problem is that uwsgi can’t find the emperor.ini file we pointed to in our service file. Let’s create that file. Most likely the directory /etc/uwsgi doesn’t exist, so create that and then the emperor.ini file in it:

mkdir /etc/uwsgi
vim /etc/uwsgi/emperor.ini

Here’s the contents of my emperor.ini:

[uwsgi]
emperor = /etc/uwsgi/vassals
uid = www-data
gid = www-data
limit-as = 1024
logto = /tmp/uwsgi.log

The last step is to create the vassals directory we just referenced in emperor.ini:

sudo mkdir /etc/uwsgi/vassals

The last step is to add a vassal, which would be the ini file for your actual uWSGI app.

To create that file, have a look at this gist over on github, it has a pretty good example. Once you have that file tweaked to your liking, just symlink it into /etc/uwsgi/vassals/. The exact paths will vary, but something like this should do the trick:

sudo ln -s /path/to/your/project/django.ini /etc/uwsgi/vassals/

Now go back and try starting uWSGI again:

sudo systemctl start uwsgi

This time it should work with no errors. Go ahead and stop it and add it to systemd so it will startup with the system:

sudo systemctl stop uwsgi
sudo systemctl enable uwsgi
sudo systemctl start uwsgi

Congratulations, your uWSGI server is now running.

Further Reading:

  • As mentioned above, this gist covers how to setup the Django end of the equation and covers more of what’s actually happening in this setup.
  • This Digital Ocean tutorial is for CentOS and related distros, but it’s what I used originally (I wrote this to keep track of all the places I changed that one).
  • The official uWSGI docs are pretty great too.

If you enjoyed this tutorial and want a VPS instance to try it out on consider signing up for Digital Ocean. It’s cheap ($5/month gets you a VPS, this site runs on a $10/month instance), fast and dare I say fun. That link will get you $10 credit, which works out to two free months of hosting and you’ll help support this site. But if you prefer here’s a link without the referral code and no $10 credit.


  1. The vast majority of this site is served from flat html files, but there are a few dynamic things like the comments that actually hit the database. For the most part though, I am the only one interacting with the Django portion of my site (which is used to build the flat HTML files I serve up to you). 

  2. I think “emperor” and “vassal” are the uWSGI project’s effort to get rid of the “slave”/”master” lingo that gets used a lot in these circumstances.