This is a tutorial for setting up a Linux, Nginx, uWSGI, Python, Django server with a PostgreSQL database. This is the easy, straightforward approach to server setup and deployment. The intended audience is developers or teams who need to get an application or two running in production without too much hassle.

If you need to oversee multiple servers or you are handling a large number of applications, the limitations of the setup described here will start to show through. Here is an example of a much more complex deployment workflow that is more appropriate reading for dedicated DevOps people.

General server setup

I’m using Linode running Ubuntu 12.04 LTS, but you should be able to adapt this tutorial to other hosts and distros as needed.

Before getting started, take steps to update and secure the server. Security is beyond the scope of this article, but if you don’t know where to start, have a look at this tutorial.

Install uWSGI

Let’s get started by installing uWSGI. ssh into the server and

sudo apt-get install uwsgi uwsgi-plugin-python

Create a user to run uWSGI:

sudo useradd -c 'uwsgi user' -g nginx --system \
--no-create-home --disabled-login --disabled-password uwsgi

Create an upstart configuration file at /etc/init/uwsgi.conf which looks like this:

description "uWSGI"
start on runlevel [2345]
stop on runlevel [06]

respawn

exec uwsgi --master --processes 4 --die-on-term --uid uwsgi --gid nginx \
--socket /tmp/uwsgi.sock --chmod-socket 660 --vhost --logto /var/log/uwsgi.log \
--plugins python

Install Nginx

The version of Nginx provided in the official Ubuntu repository is a bit outdated, so we are going to use the latest stable package from the Nginx repository. If you are on Ubuntu, just follow the instructions below, but if you are on another distro or run into trouble, have a look at the official install guide.

First we add the key

wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

Then open up the /etc/apt/sources.list file and append the following:

deb http://nginx.org/packages/ubuntu/ precise nginx
deb-src http://nginx.org/packages/ubuntu/ precise nginx

If you aren’t using Ubuntu 12.04, change precise to the the codename for your Ubuntu version.

Then install Nginx with:

sudo apt-get install nginx

Welcome to Nginx screen

Nginx is already running. To verify, open your server’s ip address in a browser, and you should see a ‘Welcome to nginx!’ screen.

Nginx config

The webapp is going to live at /var/www/<app-name>/. Here is what the directory tree will look like when we finish:

/var/www/<app-name>/
    |-- env/                    The virtual env
    |-- project/                The Django project files
        |-- <apps>/
        |-- <project>/
            |-- wsgi.py
        |-- requirements.txt    Pip project dependency list
    |-- static/                 Static files to be served by nginx     

So let’s setup that directory structure.

sudo mkdir /var/www/<app-name>
sudo mkdir /var/www/<app-name>/project
sudo mkdir /var/www/<app-name>/static

Now to setup the virtual environment for our application:

sudo apt-get install python-pip python-setuptools
pip install virtualenv
cd /var/www/<app-name>
virtualenv env

Now we need to fix some permissions so that uWSGI can read scripts and write the .pyc files:

sudo usermod -a -G nginx $USER
sudo chown -R $USER:nginx /var/www/<app-name>/
sudo chmod -R g+w /var/www/<app-name>/

Create the file /etc/nginx/conf.d/<app-name>.conf with these contents:

server {
    listen          80;
    server_name     $hostname;
    location /static {
        alias /var/www/<app-name>/static;
    }
    error_page   404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    location / {
        include         uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
        uwsgi_param UWSGI_PYHOME /var/www/<app-name>/env;
        uwsgi_param UWSGI_CHDIR /var/www/<app-name>/project;
        uwsgi_param UWSGI_MODULE <project-name>.wsgi:application;
    }      
}

Don’t forget to swap out <app-name> for the name of your app in lines 5, 15, and 16, and use your own project name on line 17.

In this configuration, nginx will serve files in the static directory, but pass off everything else to uWSGI.

Now let’s move the default config out of the way, so the new config can take it’s place.

cd /etc/nginx/conf.d
sudo mv default.conf default.conf.bak

OK let’s start things up:

sudo service uwsgi restart
sudo service nginx restart

uWSGI error screen

If everything has be setup properly so far, navigating to the IP address should now show a uWSGI error.

Install the Django app

Now install your Django app into /var/www/app-name/project/ For now, just get a clean copy out of source control and scp it to the server, or grab it directly from source control while on the server. Later, you should setup a deployment script to automate this, but for now let’s just get the app running.

In this case, I am keeping my code in a Bitbucket git repository, and I want to pull the code directly from Bitbucket to the server. So on the Bitbucket site, I create a new user with read only permissions on the code repository. Then I create a new ssh key on the server

ssh-keygen -t rsa -C "<email address>"

and add it to the Bitbucket account for the new user by uploading the id_rsa.pub file.

Now to install git

sudo apt-get install git

And pulling the source code out of the repository now looks like this:

cd /var/www/<app-name>/project/
git archive --format=tar --remote=ssh://git@bitbucket.org/<username>/<repo name>.git master | tar -xf -

Then fix up any permissions issues again:

sudo chown -R $USER:nginx /var/www/<app-name>/
sudo chmod -R g+w /var/www/<app-name>/

Install any packages required by your app or it’s dependencies. For example, psycopg2 and lxml respectively depend on the following packages:

sudo apt-get install python-dev libpq-dev
sudo apt-get install libxml2-dev libxslt-dev

Now install the project requirements into the virtual environment:

source /var/www/<app-name>/env/bin/activate
pip install -r /var/www/<app-name>/project/requirements.txt
deactivate

OK so now that the Django code files are on the server and the dependencies are satisfied, navigating to the IP address in your browser should show your app. Static files aren’t being served yet and the database isn’t installed, but we can see some progress.

Application screenshot without static files or database connection.

If you haven’t got this far, the log files for upstart, nginx, and uwsgi are a good place to start troubleshooting.

In order to have Nginx serve the static files, they need to be collected to /var/www/<app-name>/static. In settings.py point your STATIC_ROOT to that static directory, then verify that STATIC_URL and STATICFILES_DIRS are properly set up. Get any changes from the repository, then:

source /var/www/<app-name>/env/bin/activate
cd /var/www/<app-name>/project/ 
python manage.py collectstatic

Test it out in your browser and you should now see css, javascript, and image files being loaded. However, without the database, your app probably isn’t doing much of anything. So let’s fix that.

Installing PostgreSQL

sudo apt-get install postgresql postgresql-contrib

Then setup passwords:

sudo passwd postgres
su postgres
createuser django --pwprompt
psql create database <dbname> owner django
exit
sudo /etc/init.d/postgresql restart

Now initialize the database:

cd /var/www/<app-name>
source env/bin/activate
cd project
./manage.py syncdb

If you use south for database migrations, run those now.

./manage.py migrate <name>

Your Django app should now be up and running.

Closing remarks

Now that the server is ready, setup one click deploys to speed up your code – test – deploy cycle.

Support the creation of tutorials like this one by using the following links to purchase Linode hosting or shop at Amazon.

A big thanks to Conrad Kramer, Collective Disorder, and Simon Westphahl for guiding the creation of this tutorial.