How to Deploy Flask with uWSGI and Nginx

Published on Aug. 22, 2023, 12:13 p.m.

Enter uWSGI

UWSGI and its predecessors are a form of middleware for webservers.

UWSGI is objectively better than alternatives .

Getting Set Up

We need to install a bunch of Python dev packages on Ubuntu (or whatever) .

$ apt update
$ apt upgrade -y
$ sudo apt install python3.8 python3.8-dev python3-distutils uwsgi uwsgi-src uuid-dev libcap-dev libpcre3-dev python3-pip python3.8-venv

Install all Python dev dependencies

$ apt-get install uwsgi-plugin-python3

Install uWSGI Python plugin

The final bit of Ubuntu configuration is to open port 5000:

$ ufw allow 5000

Prep Your Project

Clone your project onto your VPS and make sure your project has a proper wsgi.py file .

from myapp import create_app

app = create_app()

if __name__ == "__main__":
    app.run(host='0.0.0.0')
$ python3.8 -m venv myenv
$ source myenv/bin/activate
$ python3 -m pip install -r requirements.txt

Create and build your virtual environment

uwsgi --http-socket :5000 --plugin python3 --module wsgi:app

Successful uWSGI worker spawned

Visit your server’s IP address at port 5000 .

Something awesome about uWSGI is how easy it is to utilize multiple cores in our machine by specifying how many threads .If your machine has multiple CPU cores, here’s how easy it is to use them :

$ pkill -9 uwsgi
$ uwsgi --http-socket :5000 --plugin python38 --module wsgi:app  --virtualenv /var/www/pythonmyadmin/myenv/ --processes 2 --threads 4

Keep in mind that uWSGI processes don’t get killed by simply Control+Cing .Be sure to kill unwanted uWSGI processes by using pkill -9 uwsgi.

Running uWSGI via Config File

We need a way for Nginx to hook into our uWSGI process (such as the uWSGI plugin to use, our virtual environment location, etc.).

[uwsgi]
chdir = /var/www/myapp/
module = wsgi:app

processes = 4
threads = 2
plugin = python38
virtualenv = /var/www/myapp/myenv

master = true
socket = myapp.sock
chmod-socket = 666
vacuum = true

die-on-term = true

myapp.ini

Instead of specifying http-socket here, we set socket to myapp.sock.Nginx is going to handle our HTTP requests, but it needs a way to associate incoming requests to .We handle this with this configuration, a file is created in our project directory called myapp.sock.All Nginx needs to worry about is pointing to this socket.

$ uwsgi myapp.ini

Start myapp

As an added bonus, the presence of die-on-term = true in our config means that our uWSGI process will end when we Control+C.

uWSGI & Nginx 4 Eva

$ sudo vim /etc/nginx/sites-available/myapp.conf

Create Nginx config

Listen for your domain on port 80 and forward this traffic (with parameters) to the location of the socket file:

server {
    listen 80;
    server_name example.com www.example.com;

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

myapp.conf

Note the triple slashes in the uwsgi_pass URI.

Let’s symlink this config to sites-enabled:

$ ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/myapp.conf

Restart to take effect:

$ sudo service nginx restart

Running Your App

$ cd /var/www/myapp
$ nohup uwsgi myapp.ini &

Run myapp as continuous process oppress.

Create a Service

Systemd is a Linux “service manager” to configure and run process daemons .

$ vim /etc/systemd/system/myapp.service

Create a system config

[Unit]
Description=My Python Application
After=network.target

[Service]
User=root
WorkingDirectory=/var/www/myapp
Environment="PATH=/var/www/myapp/myenv/bin"
ExecStart=/var/www/myapp/myenv/bin/uwsgi --ini myapp.ini
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Start and check your service

$ service myapp start
$ service myapp status

Systemd service status oppress.

Manage uWSGI Apps in Emperor Mode

A way to create a service per application is using uWSGI’s “Emperor” mode.Emperor mode enables us to manage all uWSGI apps globally.

Change directories to cd /etc/uwsgi.

/apps-available: A global folder to hold all your uwsgi .ini files (like the one we created earlier).This is the uWSGI equivalent of Nginx’s sites-available folder.

/apps-enabled: Just like Nginx, this folder expects symbolic links from its apps-available counterpart.Running uWSGI in emperor mode will look for all config files in this folder and run them accordingly.

Copy your config to apps-available and symlink it to apps-enabled:

$ sudo cp /var/www/myapp/myapp.ini /etc/uwsgi/apps-available/myapp.ini
$ sudo ln -s /etc/uwsgi/apps-available/myapp.ini /etc/uwsgi/apps-enabled/myapp.ini
$ sudo uwsgi --emperor /etc/uwsgi/apps-enabled/

Start emperor mode

Start on Machine Start-up

The best part about running uWSGI in emperor mode is we can have our apps launch upon machine startups.Add a file called /etc/rc.local .

/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites-enabled --daemonize /var/log/uwsgi-emperor.log

Tags: