Matt Hoffman


Deploying web.py Applications with uWSGI and nginx

I took a while deciding how I wanted to deploy Odist due to the lack of information I found on deploying a web.py app with nginx serving the content. Doing some research led me to uWSGI. A simple and powerful way of connecting the python based web.py with nginx and serving the content.

Here I will overview the entire process. I start with setting up the web.py app in a python virtual environment and using the virtualenvwrapper tool. Then I will overview setting up and configuring nginx and uWSGI to get the application up and running. This process was performed on an Ubuntu install but should be similar for most linux distributions.

Small Disclamer: Since first getting the system running I have tweaked some settings based on information found on the uWSGI manual pages. I just know that these steps will get the system talking and working together, I am not saying they are the optimal settings. I will probably make a post in the future dealing with optimization as I get a little more familiar with it.

Is Your nginx Ready?

Utilizing uWSGI necessitates compiling nginx with uWSGI's supporting plugin. You do not have to build nginx from source to have this plugin enabled. The method I used was installing nginx-full from the PPA on Launchpad. If you want to build nginx from source or are using a different linux distro there is a wealth of nginx information on the wiki.

Setting up a Python Virtual Environment

By utilizing python's virtualenv, you are able to isolate your projects to a set of python dependencies. It also makes for easy cleanup if a project is removed, without having to worry about what packages were installed in the system wide python install.

I installed virtualenv and virtualenvwrapper using python's pip package installer. Installing pip is the first necessity to get everything running. pip is included in the python-pip package. Following installing pip, the two tools are installed by running $ sudo pip install virtualenv and $ sudo pip install virtualenvwrapper. Virtualenvwrapper needs a bit more setup to run. This requires some additions to the .bashrc file in your home directory. You can find the details on the install manual page for virtualenvwrapper.

Once installed, you can then create a python virtualenv by running $ mkvirtualenv project. When working in a virtualenv you will have a shell terminal that resembles (project)$ indicating what virtualenv you are working on inside the parenthesis. In order to use web.py in this virtual environment you can then run (project)$ pip install web.py. It is important to note that the path of your virtualenv install becomes /home/<user>/.virtualenvs/project by default configuration of virtualenvwrapper. Once you have your virtualenv setup you can then install uWSGI and configure your nginx and web.py application to host your site.

uWSGI Install and Configuring nginx

Installing uWSGI is as simple as running $ sudo pip install uwsgi. If you receive an error when installing uwsgi (especially during the compile process), you may need to install the build-essential and python-dev packages (on Ubuntu). uWSGI is installed globally and includes python support from using pip. Nginx is installed in the /etc/nginx directory. Details on configuration settings for uWSGI can be found on uWSGI's project page.

To configure nginx, we make a server block entry for our application in the configuration file for the enabled site (I tend to put my site config files in the /etc/nginx/conf.d/ directory).

server {
    listen 80;
    server_name website.com www.website.com;
    access_log /var/log/website/access_log;
    location / {
        root /var/www/website.com/app;
        uwsgi_pass 127.0.0.1:3031;
        include uwsgi_params;
    }
    location /static {
        alias /var/www/website.com/app/static;
    }
}

Here we see a few additions to the server block. My application .py files are stored in /var/www/website.com/app, and then follow the general directory structure for web.py within that directory. We also tell nginx the socket that uWSGI will be using to serve the web.py application. The default port for uWSGI is 3031 but we specify this later. We also include the uwsgi_params file that is found in the /etc/nginx directory.

Starting uWSGI with Upstart

One of the challenges I had with getting the system running was getting uWSGI to start as a service, as the right user, and utilizing the right python virtualenv while serving the application.

Ubuntu now comes with Upstart for creating easy startup scripts and setting up custom services. Upstart service scripts are found in the /etc/init directory. Here we can create one for uwsgi.

# uwsgi.conf - uWSGI Service Script
description "Service Script for uWSGI"
chdir /var/www/website.com/app
env PYTHON_HOME /home/<user>/.virtualenvs/project

start on runlevel [2345]
stop on runlevel [06]

exec uwsgi --die-on-term --uid www-data -w project -T -s 127.0.0.1:3031 -p 3 -M --limit-as 256

The chdir line in the script sets our working directory to the one containing your project.py file, ready for deployment. Setting the PYTHON_HOME environment variable ensures that the correct virtualenv is being used to run the application.

uwsgi --die-on-term --uid www-data -w project -T -s 127.0.0.1:3031 -p 3 -M --limit-as 256

The magic command to get uWSGI serving the application tells uWSGI the following information:

A complete listing of the many configuration options can be found on the uWSGI website.

Upstart provides an easy way to start, stop, and check the status of services. More information on Upstart can be found in the Ubuntu Upstart Documentation.

Setting Web.py from Development to Production

The final change you may need to make before starting to serve your application is to stop web.py from using the built in webserver that you may have used for development. In your application's main .py file you probably have a block as follows if you were using the built in webserver for development.

if __name__ == '__main__':
    app = web.application(urls, globals())
    app.run()

You can now remove or comment out this block and replace it with application = web.applicaion(urls, globals()).wsgifunc() in order to tell web.py that the project will be hosted through a uWSGI server.

Start Serving your Application

Starting your application now consists of simply starting the uWSGI Upstart process with $ sudo service uwsgi start and restarting the nginx server with $ sudo /etc/init.d/nginx restart. uWSGI starts the web.py application and will serve it to nginx. Go ahead and point your browser to your running web.py application using nginx and uWSGI!!

Questions / Comments / Found an issue with something here? Feel free to send me a tweet.

April 14, 2013

Recent Reading

The Signal and the Noise: Why So Many Predictions Fail - but Some Don't Nate Silver

Crafted with Odist