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.

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 Python 3 (with venv), nginx and uWSGI. I recommend using your operating system’s packages. Make sure you are downloading the latest versions available for your OS (update the package cache first). 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 ( / ), a static hello.png file and a favicon.ico for demonstration purposes. The app is pretty basic, but all the usual advanced features (templates, user logins, database access, etc.) would work without any other web server-related config. 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.

Paths and locations This guide used to recommend creating the venv in /srv/myapp . This was changed to improve in-place Python upgrades. Virtual environments should be ephemeral, so that rm -rf $VIRTUAL_ENV is recoverable in less than 10 minutes and 2 commands. The old structure made the venv hard to delete without deleting appdata . The current structure has /srv/myapp/venv and /srv/myapp/appdata separate. An alternative structure would put the app in /srv/myapp , but that requires including venv , sockets and other deployment-specific files in .gitignore (or having dirty working directories).

We’ll start by creating a virtual environment, which is very easy with Python 3:

mkdir /srv/myapp python3 -m venv --prompt myapp /srv/myapp/venv

(The --prompt option is not supported on some old versions of Python, but you can just skip it if that’s the case, it’s just to make the prompt after source bin/activate more informative.)

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

cd /srv/myapp git clone https://github.com/Kwpolska/flask-demo-app appdata venv/bin/pip install -r appdata/requirements.txt

I’m storing my application data in the appdata subdirectory so that it doesn’t clutter the virtual environment (or vice versa). You may also install the uwsgi package in the virtual environment, 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:

Ubuntu, Debian:

chown -R www-data:www-data /srv/myapp

Fedora, CentOS:

chown -R uwsgi:nginx /srv/myapp

Arch Linux:

chown -R http:http /srv/myapp