You’ve just written a great Python web application. Now, you want to share it with the world. In order to do that, you need a server, and some software to do that for you.

The following is a comprehensive guide on how to accomplish that, on multiple Linux-based operating systems, using nginx and uWSGI Emperor. It doesn’t force you to use any specific web framework — Flask, Django, Pyramid, Bottle will all work. Written for Ubuntu, Debian, Fedora, CentOS and Arch Linux (should be helpful for other systems, too). Now with an Ansible Playbook.

In order to deploy your web application, you need a server that gives you root and ssh access — in other words, a VPS (or a dedicated server, or a datacenter lease…). If you’re looking for a great VPS service for a low price, I recommend DigitalOcean (reflink [1]), which offers a $5/mo service [2]. If you want to play along at home, without buying a VPS, you can create a virtual machine on your own, or use Vagrant with a Vagrant box for Fedora 25 (fedora/25-cloud-base).

Your server should also run a modern Linux-based operating system. This guide was written and tested on:

Ubuntu 16.04 LTS or newer

Debian 8 (jessie) or newer

Fedora 24 or newer (with SELinux enabled and disabled)

CentOS 7 (with SELinux enabled and disabled) — manual guide should also work on RHEL 7

Arch Linux

Users of other Linux distributions (and perhaps other Unix flavors) can also follow this tutorial. This guide assumes systemd as your init system; if you are not using systemd, you will have to get your own daemon files somewhere else. In places where the instructions are split three-way, try coming up with your own, reading documentation and config files; the Arch Linux instructions are probably the closest to upstream (but not always). Unfortunately, all Linux distributions have their own ideas when it comes to running and managing nginx and uWSGI.

nginx and uWSGI are considered best practices by most people. nginx is a fast, modern web server, with uWSGI support built in (without resorting to reverse proxying). uWSGI is similarly aimed at speed. The Emperor mode of uWSGI is recommended for init system integration by the uWSGI team, and it’s especially useful for multi-app deployments. (This guide is opinionated.)

Even though I personally recommend the Playbook as a much less error-prone way to set up your app, it might not be compatible with everyone’s system, or otherwise be the wrong solution. The original manual configuration guide is still maintained.

Even if you are using the Playbook, you should still read this to find out what happens under the hood, and to find out about other caveats/required configuration changes.

Note

All the commands in this tutorial are meant to be run as root — run su or sudo su first to get an administrative shell. This tutorial assumes familiarity with basic Linux administration and command-line usage.

Start by installing virtualenv, nginx and uWSGI. I recommend using your operating system packages. For uWSGI, we need the logfile and python3 plugins. (Arch Linux names the python3 plugin python; the logfile plugin may be built-in — check with your system repositories!). I’ll also install Git to clone the tutorial app, but it’s optional if your workflow does not involve git.

This tutorial will work for any web framework. I will use a really basic Flask app that has just one route (/) [3], a static hello.png file and a favicon.ico for demonstration purposes. Note that the app does not use app.run(). While you could add it, it would be used for local development and debugging only, and would have to be prepended by if __name__ == '__main__': (if it wasn’t, that server would run instead of uWSGI, which is bad)

The app will be installed somewhere under the /srv directory, which is a great place to store things like this. I’ll choose /srv/myapp for this tutorial, but for real deployments, you should use something more distinguishable — the domain name is a great idea.

If you don’t use Flask, this tutorial also has instructions for other web frameworks (Django, Pyramid, Bottle) in the configuration files; it should be adjustable to any other WSGI-compliant framework/script nevertheless.

We’ll start by creating a virtualenv:

Ubuntu, Debian:

cd /srv
virtualenv -p /usr/bin/python3 myapp

Fedora, CentOS, Arch Linux:

cd /srv
python3 -m virtualenv myapp

(This tutorial assumes Python 3. Make sure you use the correct virtualenv command/argument. If you want to use Python 2.7, you’ll need to adjust your uWSGI configuration as well.)

Now, we need to put our app there and install requirements. An example for the tutorial demo app:

I’m storing my application data in the appdata subdirectory so that it doesn’t clutter the virtualenv (or vice versa). You may also install the uwsgi package in the virtualenv, but it’s optional.

What this directory should be depends on your web framework. For example, for a Django app, you should have an appdata/manage.py file (in other words, appdata is where your app structure starts). I also assumed that the appdata folder should have a static subdirectory with all static files, including favicon.ico if you have one (we will add support for both in nginx).

At this point, you should chown this directory to the user and group your server is going to run as. This is especially important if uwsgi and nginx run as different users (as they do on Fedora). Run one of the following commands:

Parts of the configuration depend on your operating system. I tried to provide advice for Ubuntu, Debian, Fedora, CentOS and Arch Linux. If you experience any issues, in particular with plugins, please consult the documentation.

make sure it’s not in an if __name__ == '__main__': block — the demo app does that!)

uid and gid — the names of the user account to use for your server. Use the same values as in the chown command above.

processes and threads — control the resources devoted to this application. Because this is a simple hello app, I used one process with one thread, but for a real app, you will probably need more (you need to see what works the best; there is no algorithm to decide). Also, remember that if you use multiple processes, they don’t share memory (you need a database to share data between them).

plugins — the list of uWSGI plugins to use. For Arch Linux, use plugins = python (the logfile plugin is always active).

logger — the path to your app-specific logfile. (Other logging facilities are available, but this one is the easiest, especially for multiple applications on the same server)

You can test your configuration by running uwsgi --ini /path/to/myapp.ini (disable the logger for stderr output or run tail -f /srv/myapp/uwsgi.log in another window).

If you’re using Fedora or CentOS, there are two configuration changes you need to make globally: in /etc/uwsgi.ini, disable the emperor-tyrant option (which seems to be buggy) and set gid = nginx. We’ll need this so that nginx can talk to your socket.

We need to configure our web server. Here’s a basic configuration that will get us started:

Save this file as:

Ubuntu, Debian: /etc/nginx/sites-enabled/myapp.conf

Fedora, CentOS: /etc/nginx/conf.d/myapp.conf

Arch Linux: add include /etc/nginx/conf.d/*.conf; to your http directive in /etc/nginx/nginx.conf and use /etc/nginx/conf.d/myapp.conf

server{# for a public HTTP server:listen80;# for a public HTTPS server:# listen 443 ssl;server_namelocalhostmyapp.local;location/{includeuwsgi_params;uwsgi_passunix:/srv/myapp/uwsgi.sock;}location/static{alias/srv/myapp/appdata/static;}location/favicon.ico{alias/srv/myapp/appdata/static/favicon.ico;}}

Note that this file is a very basic and rudimentary configuration. This configuration is fine for local testing, but for a real deployment, you will need to adjust it:

This app does not use templates, but you should in any real project. This app is meant to be as simple as possible.

That’s all!

Hopefully, the guide worked for you and your Python web app is up and running. If so, great! Otherwise, make sure you did everything as stated in this guide. If you are still stuck, ask for help in the comments.