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.