How to serve a Flask application with uWSGI and Nginx

Published on Dec. 19, 2018 by Gabriel Bordeaux

Introduction

This is a simple introduction on how to serve a Python Flask application with uWSGI and Nginx on Ubuntu.

Prerequisite

Install Nginx and Python:

sudo apt-get update
sudo apt-get install python3-pip python3-dev nginx
pip3 install uwsgi flask

Create a basic Flask application

mkdir /var/www/html/my_app
cd /var/www/html/my_app

Create a file app.py with:

#!/usr/bin/python3

from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
    return """
        Welcome to my website!<br /><br />
        <a href="/hello">Go to hello world</a>
    """

@app.route("/hello")
def hello():
    return """
        Hello World!<br /><br />
        <a href="/">Back to index</a>
    """

if __name__ == '__main__':
    # Will make the server available externally as well
    app.run(host='0.0.0.0')

Test your application with flask built-in web server

Flask has a built in web server which you can use for test purposes. Just run:

python3 app.py
# * Serving Flask app "app.py"
# * Environment: production
#   WARNING: Do not use the development server in a production environment.
#   Use a production WSGI server instead.
# * Debug mode: off
# * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Alternatively, you can also use:

FLASK_APP=app.py flask run --host=0.0.0.0

And visit http://127.0.0.1:5000/.

Once you are able to confirm that your Flask application is up and running with the development web server, it is time to configure uWSGI and Nginx.

Configure uWSGI

In the same directory, create a file wsgi.py with:

#!/usr/bin/python3

from app import app as application

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

You can then test uWSGI:

uwsgi --socket 0.0.0.0:8000 --protocol=http -w wsgi

And visit http://127.0.0.1:8000/.

Create a uWSGI Configuration File

Also in your project directory, create a file app.ini with:

[uwsgi]
module = wsgi

socket = my_app.sock
chmod-socket = 660
vacuum = true

master = true
processes = 10

die-on-term = true

You can test the app.ini file with:

/usr/local/bin/uwsgi --ini /var/www/html/my_app/app.ini

Change the project directory ownership to www-data

chown -R www-data.www-data /var/www/html/my_app

Create a uWSGI startup file

Then create a systemctl startup file /etc/systemd/system/my_app.service:

[Unit]
Description="uWSGI server instance for my_app"
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/html/my_app/
Environment=FLASK_ENV=test
ExecStart=/usr/local/bin/uwsgi --ini /var/www/html/my_app/app.ini

[Install]
WantedBy=multi-user.target

Start the process:

sudo systemctl start my_app

# Check the status
sudo systemctl status my_app.service

# You should be able to see the socket with
ls /var/www/html/my_app/my_app.sock
# /var/www/html/my_app/my_app.sock

# Enable it on startup
sudo systemctl enable my_app

Configure Nginx

Create a file /etc/nginx/sites-enabled/my_app:

server {
    listen 80;
    server_name myapp.com www.myapp.com; # Replace with the actual domain name

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/var/www/html/my_app/my_app.sock;
    }
}

Test your Nginx configuration and restart Nginx:

nginx -t -c /etc/nginx/nginx.conf
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

/etc/init.d/nginx restart

You are all set, just visit the domain in your browser!