Even after deploying a number of Flask apps I always find myself googling up blog posts on how to get the trio of nginx, gunicorn and supervisor working together. Below is simple, straight, no-nonsense guide on how to deploy a flask app on a Linux server using Nginx, Gunicorn and Supervisor.
A disclaimer: this guide will not tell you what these technologies are. Specifically, I will not talk about why you should use nginx
+ gunicorn
instead of apache
+ mod_wsgi
. There is plenty of good documentation online which already does that. This is my preferred setup for deploying flask applications and it is extremely simple to get started.
First off setup a virtualenv. I’m a big fan of virtualenv as it helps you keep your global system environment clean.
$ cd flask_app
$ virtualenv flask_env
$ source flask_env/bin/activate
(flask_env)$ pip install flask && pip install gunicorn
With that done, lets create a bash
file called gunicorn_start
. The contents of this file are below. What this basically does is sets up the virtualenv and starts the gunicorn server on http://127.0.0.1:8000
. Do remember to customize the variables below as per your setup.
#!/bin/bash
NAME="my cool flask app"
FLASKDIR=/Code/flask_app
VENVDIR=/Code/flask_app/flask_env
SOCKFILE=/Code/flask_app/sock
USER=captain
GROUP=captain
NUM_WORKERS=3
echo "Starting $NAME"
# activate the virtualenv
cd $VENVDIR
source bin/activate
export PYTHONPATH=$FLASKDIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your unicorn
exec gunicorn main:app -b 127.0.0.1:8000 \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--log-level=debug \
--bind=unix:$SOCKFILE
To make sure everything is running, attempt a sudo ./gunicorn_start
command1. If gunicorn starts up perfectly and doesn’t cough any errors you are good to go.
Now that gunicorn
is setup properly we can now move our focus to Nginx. The configuration is quite simple to get it started. The couple of lines below simply tell Nginx to act as a reverse proxy.
Contents of flaskconfig2
server {
location / {
proxy_pass http://127.0.0.1:8000;
}
}
To run this configuration you need to save this in /etc/nginx/sites-available
. Assuming your file is flaskconfig
, you need to create a symbolic link in the sites-enabled
directory.
$ cd /etc/nginx
$ ln -s /etc/nginx/sites-available/flaskconfig /etc/nginx/sites-enabled/flaskconfig
To test everything is working fine, restart nginx - hopefully the server should restart without any server errors3. Now cd
into the project directory and start the gunicorn_start
command. Now head over to the domain name and you should see your application running.
Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems. In simple words, rather than manually starting and stopping gunicorn
you can use supervisor to create a daemon that is easy to manage. Create a new configuration file in /etc/supervisor/conf.d
. To manage supervisor you can use the familiar sudo service supervisor restart
command.
[program:flask_app]
command = /Code/flask_app/gunicorn_start
user = root
stdout_logfile = /Code/flask_app/logs/gunicorn_supervisor.log
redirect_stderr = true
Fabric is a really cool python library that can be used for application deployment and systems administration. Using nothing but python
you can create deployment / automation scripts. You can have a look at a fabfile
I created for deploying a django application.
from fabric.api import *
from contextlib import contextmanager as _contextmanager
env.user = "captain"
env.activate = "source /Code/flask_app/flask_env/bin/activate"
env.directory = "/Code/flask_app/"
env.hosts = ["33.33.33.33"]
def prepare_deploy():
local("echo ------------------------")
local("echo DEPLOYING APP TO PRODUCTION")
local("git add . && git commit")
local("git push -u origin master")
local("echo APP PUSHED TO PRODUCTION")
local("echo ------------------------")
def commit(msg):
local("git add . && git commit -am %s" % msg)
def deploy():
prepare_deploy()
with cd(env.directory):
run("git pull")
restart_service()
def restart_service():
run("sudo supervisorctl restart flask_app")
Hopefully, this post has been helpful in giving you a good idea of how you can deploy flask apps on Nginx & Gunicorn. If you have any queries feel free to contact me.
gunicorn_start
script the executable status with the chmod +x gunicorn_start
command.
[return]sudo nginx -t
to identify any configuration related errors.
[return]