Setup Nginx And uWSGI For Flask (Python Apps) On Ubuntu 16.04

December 7, 2017

Install Python

Install Python 2.x and pip.

sudo apt-get install python-dev python-pip
sudo -H pip install --upgrade pip

For Python 3.x. For pip command, use pip3 install [PACKAGE].

sudo apt-get install python3-pip python3-dev

Install Flask libraries

Create a directory for flask app.

mkdir /code
cd /code
mkdir myapp
cd myapp

Create requirements.txt with the following content.

# Latest version: https://pypi.python.org/pypi/Flask
Flask==0.12.2

Install virtualenv, create a virtualenv named env and activate it.

sudo -H pip install virtualenv
virtualenv env
source env/bin/activate

Install flask and dependencies into lib directory.

pip install -t lib -r requirements.txt

Deactivate env.

deactivate

Create flask app

Make a directory to create flask application within myapp directory.

mkdir app
cd app

Create python file app.py within myapp directory.

import os
import sys

# import libraries in lib directory
base_path = os.path.dirname(__file__)
sys.path.insert(0, os.path.join(base_path, 'lib'))

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return 'Hello'

Create python file run.py.

from app import app

if __name__ == '__main__':
    app.run()

Install uwsgi

Install Python 2.x and uwsgi.

sudo -H pip install uwsgi

Check if uwsgi is available.

uwsgi --version

Test using uwsgi to serve the flask app. Make sure the execute this within myapp directory.

uwsgi --socket 0.0.0.0:8080 --protocol=http -w run:app

Launch another ssh to test if the flask app is accessible.

curl http://localhost:8080

You can configure uwsgi service to run as service/daemon using Upstart or Systemd.

Running uWSGI via Upstart (Ubuntu 14.04)

Run in uwsgi in Emperor mode to support multiple apps.

Create /etc/init/uwsgi.conf.

cd /etc/init
sudo nano uwsgi.conf

We shall run uwsgi using nginx/www-data credential.

# Emperor uWSGI script

description "uWSGI Emperor"
start on runlevel [2345]
stop on runlevel [06]

respawn

exec uwsgi --master --die-on-term --emperor /etc/uwsgi/vassals --uid www-data --gid www-data --daemonize /var/log/uwsgi/emperor.log

Running uWSGI via Systemd (Ubuntu 16.04)

sudo nano /etc/systemd/system/emperor.uwsgi.service

We apply Group=www-data + RuntimeDirectory=uwsgi + RuntimeDirectoryMode=0775 to enable uWGI access to /var/run/uwsgi/ to write socket file.

[Unit]
Description=uWSGI Emperor
After=network.target

[Service]
Group=www-data
ExecStart=/usr/local/bin/uwsgi --ini /etc/uwsgi/emperor.ini
# Requires systemd version 211 or newer
RuntimeDirectory=uwsgi
RuntimeDirectoryMode=0775
Restart=always
KillSignal=SIGQUIT
Type=notify
# dont log to syslog, follow logto
# StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Create /etc/uwsgi/emperor.ini as uwsgi configuration file.

cd etc
sudo mkdir uwsgi
cd uwsgi
sudo nano emperor.ini
[uwsgi]
emperor = /etc/uwsgi/vassals
uid = www-data
gid = www-data
logto = /var/log/uwsgi/emperor.log
logfile-chown = www-data:www-data

Create uWSGI app configuration file

Create /etc/uwsgi/vassals/myapp.ini as uwsgi app configuration file.

mkdir vassals
cd vassals
sudo nano myapp.ini
[uwsgi]
chdir = /code/myapp
module = run:app
# python env
home = /code/myapp/env
# touch this file to reload the app
touch-reload = /code/myapp/run.py
logto = /var/log/uwsgi/myapp.log

# master with 2 worker process (based on CPU number)
master = true
processes = 2

# use unix socket for integration with nginx
socket = /var/run/uwsgi/myapp.sock
chmod-socket = 660
# enable socket cleanup when process stop
vacuum = true

# ensure compatibility with init system
die-on-term = true 

Create /var/log/uwsgi/.

sudo mkdir /var/log/uwsgi/
sudo chown www-data:adm /var/log/uwsgi/
sudo chmod 755 /var/log/uwsgi/

Start uwsgi.

sudo service emperor.uwsgi start

Enable the service (autostart when system boot).

sudo systemctl enable emperor.uwsgi

You can also use the following commands.

systemctl start emperor.uwsgi.service
systemctl status emperor.uwsgi.service
systemctl stop emperor.uwsgi.service
systemctl restart emperor.uwsgi.service

Note: if there are error starting uWSGI, you can check the following logs

  • sudo tail -f /var/log/syslog
  • sudo tail -f /var/log/uwsgi/emperor.log
  • sudo tail -f /var/log/uwsgi/myapp.log

Note: you can also configure one service per app in systemd (not using emperor mode).

Install and Setup Nginx for uWSGI

Install Nginx.

sudo apt-get install nginx

Test if Nginx is working.

curl http://localhost

Make a copy of default nginx configuration file.

cd /etc/nginx/sites-available
sudo cp default myapp

Edit the myapp configuration file.

sudo nano myapp

Configurations which require edit are commented as // EDIT:.

server {
        ...
        // EDIT: remove default_server
        listen 80;
        listen [::]:80;

        // EDIT:
        server_name [DOMAIN_NAME];

        // EDIT:
        location / {
                # try_files $uri $uri/ =404;
                include uwsgi_params;
                uwsgi_pass unix:/var/run/uwsgi/myapp.sock;
        }
}

Enable our new site.

sudo ln -s /etc/nginx/sites-available/food /etc/nginx/sites-enabled

Check if there are any syntax error.

sudo nginx -t

Restart nginx. You can use sudo service nginx restart as well.

sudo systemctl restart nginx

Test if ngix is serving the Flask up through uWSGI.

curl http://[DOMAIN_NAME]

Allow Nginx port for firewall

List the application configuration which ufw understand.

sudo ufw app list

Available applications:

  • Nginx Full (HTTP + HTTPS)
  • Nginx HTTP
  • Nginx HTTPS
  • OpenSSH

You can choose HTTP or Full (if you plan to enable SSL later)

sudo ufw allow 'Nginx HTTP'

Verify if the changes is successful.

sudo ufw status

Now you can test to access your website from a external browser http://[DOMAIN_NAME].

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.