How to Set Up Django with Nginx, uWSGI & systemd on Debian/Ubuntu
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.
-
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). ↩
-
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. ↩
2 Comments
thanks really save me a lot of work
@simon-
Glad it helped.
Thoughts?
Please leave a reply:
All comments are moderated, so you won’t see it right away. And please remember Kurt Vonnegut's rule: “god damn it, you’ve got to be kind.” You can use Markdown or HTML to format your comments. The allowed tags are
<b>, <i>, <em>, <strong>, <a>
. To create a new paragraph hit return twice.