Setting up Nginx + uWSGI Emperor to host Python applications on Ubuntu 12.04

This is the first of many posts on how our server is set up to serve Python web applications and others. For this post, we’ll focus on setting up Nginx and uWSGI and getting a bare-bones Django application running and served by Nginx.

Installing Nginx

Nginx is available in the Ubuntu software repositories, so installing it is pretty simple.

$ apt-get install nginx

Test your installation by starting Nginx:

$ /etc/init.d/nginx start

You should see a message along the lines of "starting nginx".

Check out Linode’s impressive library for a more thorough walkthrough on installing Nginx.

Installing uWSGI

uWSGI is available in the Ubuntu repositories as well, and also on PyPI. It’s up to you which method you prefer.

$ apt-get install uwsgi
# or
$ pip install uwsgi
Setting up our Django project

First thing we need to do is prepare some folders on where our project will live. We use the structure below for our projects:

$ mkdir /var/www/
$ cd /var/www/
$ mkdir venv conf src logs

/var/www/ `- |- venv/ |- conf/ | |- nginx.conf (include in main nginx.conf) | `- uwsgi.ini |- src/ | `- example/ ( startproject example) `- logs/ |- access.log (nginx access log) `- error.log (nginx error log)

This is where the virtual environment lives.

$ virtualenv /var/www/
$ source /var/www/
(venv)$ pip install django

This is where the project’s source code lives.

$ mkdir /var/www/
$ cd /var/www/
(venv)$ startproject example

This is where configuration files for Nginx and uWSGI are stored.


# variables
projectname = example
projectdomain =
base = /var/www/
# config
protocol = uwsgi
venv = %(base)/venv
pythonpath = %(base)/src/%(projectname)
module = %(projectname).wsgi
socket = /tmp/%(projectdomain).sock
logto = %(base)/logs/uwsgi.log 

The .ini template above only works for Django >= 1.4. For Django < 1.4, check out uWSGI’s wiki for a template.


server {
  listen 92;
  root /var/www/;
  access_log /var/www/;
  error_log /var/www/;

location / { include uwsgi_params; uwsgi_pass unix:///tmp/; } }

You’ll need to modify your main nginx.conf to include the project’s nginx.conf. Assuming Nginx was installed in /etc/nginx/, the main nginx.conf is /etc/nginx/conf/nginx.conf:

user    www-data;
# ...
http {
  # ...
  include /var/www/*/conf/nginx.conf;   
  # ...

That include statement will include any nginx.conf files in all projects following the structure outlined earlier.

We can now test if our application is being served on port 92 as specified in the nginx.conf.

# restart nginx
$ /etc/init.d/nginx restart
# run uWSGI pointing to our project's .ini file
# you can comment out the `logto` setting in
# uwsgi.ini to see log messages in the terminal
$ uwsgi --ini /var/www/

Now open up a new terminal to your server and run:

$ curl http://localhost:92

If everything goes well, it should output the "Congratulations!" page from Django.

If not, check your conf files and/or the directories. Depending on your permissions, you might need to run uwsgi with sudo to allow uWSGI to create the /tmp/*.sock and /var/log/uwsgi/*.log files.

uWSGI Emperor

Emperor is a feature that monitors multiple uWSGI applications.

Let’s try running the app via Emperor:

$ uwsgi --emperor "/var/www/*/conf/uwsgi.ini"

What this does is it runs uWSGI’s Emperor mode looking for any uwsgi.ini files for all apps living in /var/www/ and starting/stopping/restarting them as needed. Any new additions will be automatically started, no need to restart Emperor itself. If an existing uwsgi.ini is modified, it is also automatically restarted.


We’re almost done. We just need a way to manage the uWSGI Emperor process. We use Supervisord to do this.


command=/usr/local/bin/uwsgi --emperor "/var/www/*/conf/uwsgi.ini" --die-on-term --master --uid www-data --gid www-data --logto /var/log/uwsgi/emperor.log

Don’t forget to create the /var/log/uwsgi/ folder for the uWSGI logs. Again, depending on your permissions, you might need to change its user:group to www-data.

We use this init script to manage Supervisord.

Let’s test everything again.

$ /etc/init.d/nginx restart
$ supervisord
$ curl localhost:92

If you’re having trouble or if some part isn’t clear or incorrect, don’t hesitate to drop me an email or leave a comment below.

update: Changing so nginx run as www-data.