Hosting Multiple Flask Apps Using uWSGI + Nginx

I’ve got multiple Flask apps running under one domain, so I’m writing this post to describe the way I have that set up.

I will not be prepending all the commands with sudo, although you should.

The packages

The packages we need include uWSGI, Nginx, virtualenv, and pip. Use the following command to install them:

apt-get install uwsgi uwsgi-core uwsgi-plugin-python3
apt-get install nginx
apt-get install python-virtualenv python3-pip

Virtualenv is used rather than pyvenv because as of the time of writing, Ubuntu 14.04 does not support pyvenv.

Flask virtualenv

Let’s assume your Flask app is in /var/www/example_app. The assumption is also that the actual module is /var/www/example_app/app and the callable object is app. The callable is the object with the .run() method.

First, we’ll need to set up the virtual environment.

NOTE: You need the sudo su - line, or else source and pip won’t work.

cd /var/www/example_app
virtualenv --python=python3 venv
sudo su -
source venv/bin/activate
pip install -r requirements.txt

uWSGI configs

First let’s create the directories withing which we’ll keep the uWSGI configs.

mkdir -p /etc/uwsgi/apps-available
mkdir -p /etc/uwsgi/apps-enabled

For uWSGI upstart file, I use the following.

# uwsgi - uWSGI

description "uWSGI Emperor"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

env UWSGI=/usr/bin/uwsgi
env LOGTO=/var/log/uwsgi/emperor.log

exec $UWSGI --master --emperor /etc/uwsgi/apps-enabled --logto $LOGTO --die-on-term --pidfile /tmp/uwsgi-emperor.pid

This assumes all the config files for your apps are within /etc/uwsgi/apps-enabled. I use this similarly to /etc/nginx/sites-enabled. The emperor will run uWSGI processes for each of the config files within apps-enabled and restart them if there are crashes.

I’m hosting multiple apps under one domain, so the last two lines of my uWSGI config files deal with stripping the script names from the URLs (so that your code can use @app.route('/') rather than @app.route('/example_app/')).

The uWSGI config files are generic (they’re all identical except for the port number). The %n refers to the name of the file. If the file is called example_app.ini, then %n will be replaced with example_app.

[uwsgi]
base = /var/www/%n
master = true
module = app
callable = app
plugin = python
uid = www-data
gid = www-data
home = %(base)/venv
pythonpath = %(base)
socket = 127.0.0.1:3034
logto = /var/log/uwsgi/%n.log
pidfile = /tmp/uwsgi-%n.pid
touch-reload = /var/www/%n/UWSGI_RELOAD
manage-script-name = true
mount = /%n=%(module):%(callable)

The last two lines tell uWSGI to strip off the script name before passing the URL to the Flask app. Since it’s using %n, it assumes your app is accessible using ‘http://example.com/example_app/’.

The touch-reload option tell uWSGI to reload your app if there are changes to the /var/www/example_app/UWSGI_RELOAD file. You use this by updating your code and then executing touch /var/www/example_app/UWSGI_RELOAD.

Put the above config within /etc/uwsgi/apps-available and symlink it to /etc/uwsgi/apps-enabled:

ln -s /etc/uwsgi/apps-available/example_app.ini /etc/uwsgi/apps-enabled/

The uWSGI emperor should automatically detect changes to the above

Nginx config

Now that we’ve got uWSGI running, we’ll need to expose it to the outside world using Nginx. Note that you have to set the port number to the same one you have within your uWSGI config file.

server {
        server_name example.com;
        root /var/www;
        error_page 404 502 403 /error.html;

        location /error.html {
                root /var/www/default/static/;
        }

        location /example_app/ {
                include uwsgi_params;
                uwsgi_pass 127.0.0.1:3034;
        }
}

Reload your Nginx config:

service nginx reload

Now you should be able to access your site by going to ‘http://example.com/example_app’.

If there are any issues first make sure that you have all your code within the paths mentioned in the config files (modifying the paths if needed). Also, remember to check the /var/log/uwsgi/example_app.log file for errors specific to your app.