Flask App Deployment

Deploy python backend application using flask, gunicorn and nginx on Ubuntu

This guide needs slight modification if you want to run the flask / python app in python virtual environment

Flask App

Flask is a web framework, it’s a Python module that lets you develop web applications easily. So suppose you have a code which converts Json data to a more structured Excel format for easy processing, then you can use flask module to offer this task as a web service.

You do this by importing the flask module in you python code and creating an app with the designated route as below

from flask import Flask

app = Flask(__name__)
@app.route("/")

def helloworld():
    return "Hello World!"

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

The app.run() will start the flask app server and the server will start to listen to the incoming requests. The @app.route(“/”) means that, when we hit the URL to “/” it will call the helloworld function and run the code and return the result from the function. So as you run this code the flask server will show a local url (127.0.0.1:5000/), which can be accessed via a web broswer to see the result.

But flask has a catch. Its documents says: While lightweight and easy to use, Flask’s built-in server is not suitable for production as it doesn’t scale well and by default serves only one request at a time.

So we nowintroduct Gunicorn. Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. Gunicorn is built so many different web servers can interact with it. It also does not really care what you used to build your web application - as long as it can be interacted with using the WSGI interface. Gunicorn takes care of everything which happens in-between the web server and your web application.

So lets see how to connect the Flask web app to Gnicorn So you python / flask app is resting in /usr/local/bin Now create a wsgi.py to create an interface between flash and gunicorn. Here app from the flask app is implemented as WSGI

from flask_app_filename import app

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

Before we move on to the next step, note the the flask app needs to be served on all ip. This is done by specifying the ip in the app.run() in you flask app. You may change the debug flag to False for production enviromnet.

app.run(debug=False, host="0.0.0.0")

Gunicorn Server

Now its time to start the Gunicorn server. Before that install Gunicorn using pip install Gunicorn Now we create a config file for Gunicorn. This is optional as the parameters cab be passed while running the command, but got convenienece we create a config file at /usr/local/bin/gunicorn_config.py

workers = 2  # Adjust based on your server's resources
bind = '0.0.0.0:5000' # Listen to all IP on port 5000

Now run gunicorn using below command. If you face some issue, check the path of your wsgi.py and gunucorn_config.py.

gunicorn -c /usr/local/bin/gunicorn_config.py wsgi:app

NGINX Server

Next we need to configure NGINX server to serve this app in frontend, to the WWW. This involved creating a nginx config file in sites-available, Then linking it to sites-enables and finally by restarting rthe NGINX server.

in /etc/nginx/sites-available create a yourapp config file with below content. Replaace the example.com with you own domain name. You can ge the SSL certificate from let's encrypt for you domain. This config will only serve HTTPS request.

server {
    listen 80;
    listen [::]:80;
    server_name example.com;

    # Prevent nginx HTTP Server Detection
    server_tokens off;

    # enforce https
    return 301 https://$server_name:443$request_uri;
    }

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.ccom/privkey.pem;

    location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:5000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

For HTTP only app use this config file.

server {
    listen 80;
    listen [::]:80;
    server_name example.com;

    location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:5000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
}

now test the nginx configuration using

sudo nginx -t

If there is no error the restart nginx server and you flask app will not be sweved at your domain name example.com

sudo systemctl restart nginx

or

sudo service nginx restart

Gunicorn Serivce Creation

Please note that Gunicorn server will stop once the bash prompt is colsed. So to run it in background as well as to restart it on system reboot create the following systemd service config file your_app.service with below content. Replace the User field with your Ubuntu user name.

[Unit]
Description=Gunicorn instance to serve your_app
After=network.target

[Service]
User=your_user
Group=www-data
WorkingDirectory=/usr/local/bin
ExecStart=gunicorn -c /usr/local/bin/gunicorn_config.py wsgi:app

[Install]
WantedBy=multi-user.target

If you are running the python / flask app in virtual environment you will need an Environment variable in the above service config file.

Environment="PATH=/path/to/venv/bin"

Reload the systemd manager configuration to make it aware of the new service file

sudo systemctl daemon-reload

Enable the service to start on boot (name of your service config file your_app)

sudo systemctl enable your_app

Start the service

sudo systemctl start gunicorn

Verify that the service is running without errors

sudo systemctl status gunicorn

Stop the service

sudo systemctl stop gunicorn

Last updated