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