How to deploy Flask app on Ubuntu 20.04 with Nginx and Gunicorn
Lets define minimum conditions of acceptable deployment as:
- Application sits behind “proper” web server in this case Nginx;
- Application runs on ”proper” application server in this case Gunicorn;
- Application startup or shutdown is managed by native Ubuntu service manager in this case systemd;
- Application data, configuration and install folders are separated from each other so they can be independently backed up and restored.
- Application upgrade is done by executing pip install –upgrade.
Create Flask Demo Application
We need a Flask application to deploy, lets build the “simplest” one. I’ll develop app on an Ubuntu machine with Python 3.8 and Poetry, you can use whatever you are familiar with.
In case you are developing on Ubuntu 18.04/20.04 you can prepare development environment with:
$ sudo apt install python3.8 python3.8-dev python3.8-venv $ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3.8 -
Create “demoapp” application directory and initialize Python virtual environment (venv):
When executing poetry init
accept all defaults answers except for questions about defining dependencies interactively where you should choose “no”.
Poetry will create venv inside ~/.cache/pypoetry/virtualenvs/demoapp-xxxx
just for this application. That you can activate by executing poetry shell
inside ~/demoapp
.
Activate python venv and create demoapp directory structure:
(demoapp-xx) $
in front of prompt indicates that python venv is active.
Add following lines inside demoapp/pyproject.toml
at end of [tool.poetry] section just before [tool.poetry.dependencies]:
Create demoapp/__init__.py
:
Create demoapp/templates/index.html
:
You will notice that I’m preparing this app to be packaged for easy deployment by setting up instance_path and instance_relative_config to Flask and by adding packages directive in pyproject.toml.
To test your app run inside ~/demoapp
:
Open up http://localhost:5000 in the browser and you should see page displaying “Hello from Demo application”.
Prepare “demoapp” Package for Deployment
Create demoapp-0.1.0-py3-none-any.whl and demoapp-0.1.0.tar.gz distribution packages inside the ~/demoapp/dist
:
Configuring Ubuntu 20.04
I’ll assume following:
- You have root privileges on fresh installation of Ubuntu 20.04;
- Acme company has built this “demoapp”;
- You’re creating an “acme” user to be responsible for managing demoapp;
- You’re hosting the app on a public server named server.acme.com;
- You know how to setup domain name to point on your server
Add User Responsible for Managing Application
Upgrade system, add acme user and allow acme to sudo as root:
Install Nginx
Go to http://server.acme.com to check if website is running.
If browser is constantly trying to redirect you to https://server.acme.com use browser incognito/private mode to access website over http.
Configure Nginx to Use HTTPS With “letsencrypt.org” Certificate
I’ll use acme.sh script to obtain and renew certificates. Install it with:
Close and reopen your terminal to start using acme.sh.
Obtain and install certificate:
acme.sh will add cronjob to renew and reinstall certificates when they expires.
Import Certificates in Nginx
Change /etc/nginx/sites-enabled/default
to:
Now every HTTP request will be redirected to HTTPS. This is good for now but we’ll change this after installing our demoapp.
Enable Firewall
Use the simplest firewall configuration as:
Check firewall status with # ufw status
.
Deploy Flask Application
We’ll install our Flask “demoapp” under /opt/acme
as user acme.
Preparing venv and Directory Structure
Install appropriate python version (in this case Python 3.8):
Create deployment directory structure:
Following dirs are created for:
- /opt/acme/bin: all bash scripts realated to our demoapp;
- /opt/acme/envs: python virtual enviroments;
- /opt/acme/demoapp: application configuration and data files;
Create venv:
Activate venv:
Activate venv with .bash_aliases
If you are lazy as me, you can add following line in ~/.bash_aliases
, to activate venv by typing demoapp
:
After you close and reopen your terminal you can type demoapp
to activate python venv
and go to application directory.
Install “demoapp”
(demoapp) $
indicates that you should run following commands in python venv for demoapp. I’m also assuming that you already copied package with flask demoapp in/home/acme
.
Directory
/opt/acme/demoapp/instance
is flask instance folder.
I usually use
/opt/acme/demoapp/data
to save all app data that need to be saved directly on hard disk.
Create /opt/acme/demoapp/instance/config.py
:
You can generate random secret key with:
Install and Configure Gunicorn
Create /opt/acme/demoapp/gunicorn.conf.py
:
Settings in gunicorn.conf.py
file will tell gunicorn how to start our demoapp and also will tell demoapp where it is its INSTANCE_PATH directory that contains application configuration.
Create systemd Scripts for Starting and Stoping Gunicorn
/etc/systemd/system/demoapp.socket
:
/etc/systemd/system/demoapp.service
:
Execute:
You can use sudo systemctl status|start|stop|enable|disable demoapp.service
commands to manage or check status of demoapp services.
Configure Nginx to Proxy Requests to Gunicorn
Open /etc/nginx/sites-enabled/default
configured previously and change location /
directive to:
Serve Flask App with URL Prefix
Open /opt/acme/demoapp/instance/config.py
and add line:
Open /etc/nginx/sites-enabled/default
and change location /
directive to:
How to Upgrade App
Copy new version of demoapp
into /home/acme
and login to server:
When you are installing or upgrading “real” Flask app that uses database, saves data on
local storages and access other system or network resources you will probably need to execute
additional commands like flask migrate
etc.
Feedback
If you have any feedback or you want to see more posts like this, let me know on Twitter.