How to setup Flask with gunicorn and nginx with example

Introduction

This guide will help you to setup a Flask app using gunicorn and nginx. It will cover also how to do a load balance with many instances. You need to be familiar with virtualenv and some minimal python.

Step 1: Install requirements

Install nginx and python dependencies

sudo apt update
sudo apt install python-pip python-dev nginx

Step 2: Install python dependencies

Prepare the virtualenv. In this guide we will use virtualenv wrapper. If you want to know more about virtualenv wrapper installation check here.

sudo pip install pip -U  # update pip
sudo pip install virtualenvwrapper
# next command are for using virtualenvwrapper with bash
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
source /usr/local/bin/virtualenvwrapper.sh
mkvirtualenv flask_app

All your virtualenv will be saved at your home directory under the name .virtualenv. Now it's time to isntall your flask app deps:

workon flask_app
pip install -r requirements.txt #  or python setup.py install

You are done with dep installation, continue to step 3.

Step 3: Congirue gunicorn to work with flask

Install gunicorn with:

pip install gunicorn

Create a wsgi module like this one:

from myproject import application

if __name__ == "__main__":
    application.run()

Start gunicorn with:

gunicorn --bind 0.0.0.0:8000 wsgi

Test with your browser at your ip address and port 8000 or localhost http://127.0.0.1:8000 If everything if working, you can configure gunicorn to start at boot with step 4.

Step 4: Configure gunicorn at boot

We are going to use systemd to start the app at boot. Add a new file at /etc/systemd/system/ with .serice extension and with this content: If you every need to debug systemd, remeber to use journalctl command (example sudo journalctl -u flask_app.service)

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
PIDFile=/home/application_user/app.pid
User=someuser
Group=someuser
RuntimeDirectory=gunicorn
WorkingDirectory=/home/application_user/flask_app
ExecStart=/home/application_user/.virtualenvs/flask_app/bin/gunicorn --pid /home/application_user/app.pid --workers=8  \
          --bind unix:/home/application_user/app.socket applicationname.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Remeber to modify the settings according to your setup. We used 8 workers since we had a 8 core cpu.

Add the .socket file at /etc/systemd/system/gunicorn.socket with:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn/socket

[Install]
WantedBy=sockets.target

And finnaly add the file /etc/tmpfiles.d/gunicorn.conf with:

d /run/gunicorn 0755 someuser somegroup -

Enable the service with:

systemctl enable gunicorn.socket

Finnaly start the service with:

systemctl start gunicorn.socket

Step 5: Configure nginx with gunicorn

Add a new file to /etc/nginx/sites-available with this content:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include proxy_params;
        proxy_pass unix:/home/application_user/app.socket;
    }
}

Enable the site with:

sudo ln -s /etc/nginx/sites-available/flask_app /etc/nginx/sites-enables/flask_app
sudo service nginx restart

Step 6: Load balance with multiple hosts usign nginx

We will use 3 computers with the backend running using gunicorn. Each will use 8 workers. This time it will bind to port 8000.

The ExecStart we used was the following:

ExecStart=/home/application_user/.virtualenvs/flask_app/bin/gunicorn --pid /home/application_user/app.pid --workers=8\
          --bind 0.0.0.0:8000 applicationname.wsgi

Then we created an upstream on nginx using this settings:

http {
    upstream app_cluster {
        server 192.168.0.2:8000;
        server 192.168.0.3:8000;
        server 192.168.0.4:8000;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://app_cluster;
        }
    }
}