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

Dec 7, 2017

Install Python

Install Python 2.x and pip.

sudo apt-get install python-dev python-pipsudo -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 /codecd /codemkdir myappcd 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 virtualenvvirtualenv envsource 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 appcd app

Create python file app.py within myapp directory.

import osimport sys# import libraries in lib directorybase_path = os.path.dirname(__file__)sys.path.insert(0, os.path.join(base_path, 'lib'))from flask import Flaskapp = Flask(__name__)@app.route('/')def home():    return 'Hello'

Create python file run.py.

from app import appif __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/initsudo 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 etcsudo mkdir uwsgicd uwsgisudo 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 vassalscd vassalssudo 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
req-logger = file:/var/log/uwsgi/myapp-request.log
logger = file:/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.servicesystemctl status emperor.uwsgi.servicesystemctl stop emperor.uwsgi.servicesystemctl 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-availablesudo 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:

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.