The following is a tutorial on how to set up uWSGI with an Nginx front end to server simple Python scripts.

In this tutorial we will only use the packages that are supplied by Ubuntu.

It was tested on an Ubuntu 13.10 x64 droplet of Digital Ocean.

After you create a droplet with Ubuntu 13.10 x64 you'll get an e-mail with your IP address and the password of root. In this example I'll use 1.2.3.4 as the IP address. You'll have to replace the commands with the IP address of your server.

First just ssh to the server. On Linux/Unix/OSX you would type this:

$ ssh root@1.2.3.4

On Windows you'd probably install putty and use that.

Once you are logged in you need to update the packages to the latest by typing the following:

# aptitude update
# aptitude safe-upgrade

Then reboot:

# reboot

This will disconnect you from the server. After a few seconds you can continue:

I'd recommend copying your public ssh key to let you ssh without password:

$ scp ~/.ssh/id_rsa.pub root@1.2.3.4:.ssh/authorized_keys
$ ssh root@1.2.3.4

If the first command worked well, then the second won't ask for a password.

Install uWSGI

# aptitude install uwsgi
# aptitude install uwsgi-plugin-python

Just to make sure, check the versions we have. I got the following:

# python -V
Python 2.7.5+
# uwsgi --version
1.9.13-debian

Then create a user called dev so we won't do everything as root.

# adduser --gecos '' --disabled-password  dev

Switch to the new user and create a directory for the project.

# su - dev 
$ mkdir project
$ cd project/

In the project/ directory create a file called app.py with the following content:

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return "Hello World From Python"

Run the following script:

$ uwsgi --http-socket :9090 --plugin python --wsgi-file app.py 

Now you can already visit the we site by following th URL: http://1.2.3.4:9090 (remember to replace the IP with the one you have).

Without including the python plugin, if I only run

$ uwsgi --http-socket :9090 --wsgi-file app.py 

I'd get the following error:

uwsgi: unrecognized option '--wsgi-file'
getopt_long() error

Further uWSGI configuration (3 processes handle the requests) can be provided on the command line:

$ uwsgi --http-socket :9090 --plugin python --wsgi-file app.py --process 3

But, instead of the command line, it is probably better to create a configuration file called /home/dev/project/project-uwsgi.ini with the following content:

[uwsgi]
http-socket    = :9090
plugin    = python
wsgi-file = /home/dev/project/app.py
process   = 3

Now we can launch the server using the following command:

uwsgi --ini project-uwsgi.ini

We can shut it down by pressing Ctrl-C.

Then we switch back to user root typing

$ exit

We can then create a symbolic link so uWSGI will start our server automatically when the server boots up:

# ln -s /home/dev/project/project-uwsgi.ini /etc/uwsgi/apps-enabled/

now we can launch the service as root with the following command:

# service uwsgi start

Add Nginx to the mix

First thing, replace http-socket by socket in project-uwsgi.ini file.

Install Nginx and remove the default configuration file:

# aptitude install nginx
# service nginx start
# rm /etc/nginx/sites-enabled/default

Instead of that create a new configuration file in /home/dev/project/nginx-uwsgi.conf with the following content:

server {
  location /hello/ {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:9090;
  }
}

Create a symbolic link in the directory of Nginx so when Nginx starts this configuration file is taken in account.

# ln -s /home/dev/project/nginx-uwsgi.conf /etc/nginx/sites-enabled/
# service nginx restart

Now you can visit http://1.2.3.4/hello and see the output of the same script as you saw earlier.

Show the environment

Edit the /home/dev/project/app.py file to have the following in it.

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])

    html = "<h1>Hello World From Python</h1>\n"
    html += "<table>\n"
    for k in env:
        html += "<tr><td>{}</td><td>{}</td></tr>\n".format(k, env[k])
    html += "</table>\n"

    return html

and visit your home page again. You'll see all the environment it receives.

Add echo form

Update the script again to include a form and to echo back whatever the user typed in:

import cgi

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])

    html = "<h1>Hello World From Python</h1>\n"
    html += "<table>\n"
    for k in env:
        html += "<tr><td>{}</td><td>{}</td></tr>\n".format(k, env[k])
    html += "</table>\n"
    html += "<form>\n"
    html += '<input name="txt" />\n'
    html += '<input type="submit" value="Echo" />\n'
    html += "</form>\n"


    form = cgi.FieldStorage(environ=env)
    if 'txt' in form:
        html += "<hr>You said: <b>{}</b>\n".format(form['txt'].value)

    return html

Of course you'd probably not build a real application this way, but it is a good way to play with the environment and see that everything works fine.

It can also be very useful to write small web interfaces.

502 Bad Gateway

If, while already trying to use Nginx you happen to get the above error in the browser and you might also get something like this:

upstream prematurely closed connection while reading response header from upstream, client: 192.117.127.193, server: , request: "GET / HTTP/1.1", upstream: "uwsgi://127.0.0.1:9090", host: "159.203.101.19"

in /var/log/nginx/error.log the error log on Nginx, then you probably have forgotten to replace http-socket by socket in the project-uwsgi.ini> file or you have forgotten to reload uwsgi after doing so. This happened to me as well, and took me quite some time to figure out the problem.