Running a Flask App as a Service on Linux

Prerequisites

  • Linux environment

Description

This post describes how to run a Flask app on Linux as a service using Systemd. It is assumed that the service is accessed from the same network using an IP and Port number.

Prepare Linux Environment

Update package lists

Before you start we need to have a few things installed. First run the following command from your Linux environment.

sudo apt-get update

This downloads the package lists from the repositories and updates the information on the newest versions of packages and their dependencies.

Install Nginx

NOTE: You can skip this step if you do not need to host your service behind a web server like Nginx. It will not be used in the remainder of this post.

If you don’t already have it installed, you must install Nginx.
It easy to install Nginx with a single command:

sudo apt-get install nginx

Nginx will start automatically.

By default, Ngnix creates a web server root directory at /var/www/html/ with a default html web page in the directory. When nginx is up and running, the webpage will be visible by pointing the browser url to your Linux machine.

If for some reason Nginx does not start automatically, start the server with the following command:

sudo service nginx start

Install Gunicorn

You can do this either in a python or conda virtual environment. In this example I will use the base conda environment.

Activate the environment and install Gunicorn by typing

pip install gunicorn

Create working directory

Create a directory called hello_service in which you will keep your python application.

cd ~
mkdir hello_service

Create a Flask App

For the Flask application we will create a simple hello world app.

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['GET'])
def hello():
    return 'Hello World'
	
if __name__ == "__main__":
	app.run(host='0.0.0.0')

Save this file in the hello_service folder.

Create WSGI entry point

Next, create a file called wsgi.py that will serve as the entry point for our application code in app.py.

From inside the hello_service folder type the following to create it using nano, or use a text editor to create it.

nano wsgi.py

The Flask application code will be imported from app.py inside the entry point file:

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

Save and close the file.

Testing with Gunicorn

Check that Gunicorn can serve the application correctly. We can do this by simply passing the gunicorn command the name of our entry point file (minus the .py extension), plus the name of the application. In this case, it is wsgi:app. We’ll also specify a publicly available interface and port to bind to. From the working directory hello_service run the following command:

gunicorn --bind 0.0.0.0:5000 wsgi:app

Using a browser, visit your server’s IP address on port :5000 to see your application:

http://your_server_ip:5000/hello

When you have confirmed that it’s functioning properly, press CTRL-C in the terminal to stop the application.

Create Systemd Unit File

The systemd unit file must be saved to the system folder at /etc/systemd/system. Run the following command to create it using nano:

sudo nano /etc/systemd/system/hello_world.service

Inside the file add the following

[Unit] 
Description=Gunicorn instance to serve hello world app 
After=network.target 
 
[Service] 
WorkingDirectory=/home/sal/hello_service 
Environment="PATH=/home/sal/miniconda3/bin" 
EnviromentFile=/home/sal/.bashrc 
ExecStart=/home/sal/miniconda3/bin/gunicorn --workers 1 --bind 0.0.0.0:5001 wsgi:app 
 
[Install] 
WantedBy=multi-user.target

Here is a description of each of the items in the unit file.

[Unit]

This section is used for defining metadata for the unit and configuring the relationship of the unit to other units.

Description - Describes the name and basic functionality of the unit.

After - The units listed in this directive will be started before starting the current unit. In this case network.target will be started before the unit for our service.

[Service]

This section is used to provide configuration that is only applicable for services.

WorkingDirectory - This is the folder where we saved the application app.py

Environment - This is the path to the python executable for your environment. If using conda, this is typically found in the .conda folder in the /envs/environmentname/bin sub folder. In my case I’m using the base environemnt to the subfolder is miniconda3/bin

EnvironmentFile - This is the Path to your .bashrc file. Make sure

ExecStart - This is the command to run. We are launching gunicorn and it is located in the bin folder. The entry point file and name of application will be located in the working directory.

[Install]

This section is mandatory for autostart services, because it tells systemd at which moment during boot process your service should be started. Your process should be linked to some generic boot targets such as multi-user.target or graphical.target

WantedBy - The WantedBy dependency establishes a relationship between the current unit and the dependency unit without modifying the dependency unit. The current unit has WantedBy=multi-user.target, a directory called multi-user.target.wants will be created within /etc/systemd/system (if not already available) and a symbolic link to the current unit will be placed within. Systemd will start your service whenever multi-user target is started.

Save the file to the /etc/systemd/system folder and close the file.

Run the service

First run daemon-reload. daemon-reload reruns all generators, reload all unit files, and recreate the entire dependency tree.

systemctl daemon-reload

enable command enables the service to start automatically at boot.

systemctl enable hellowworld.service

start starts the service

systemctl start helloworld.service

Use status to check that its running

systemctl status helloworld.service

You will see the following

If you restart your linux server now, the service should automatically start up on boot. If you visit http://your_server_ip:5001/hello you should get the response string Hello World.

Disable the service

To disable the service from starting automatically at boot, you can type

sudo systemctl disable hellowworld.service

This will remove the symbolic link that indicated that the service should be started automatically.

Troubleshooting

If there are issues try checking the sytem log for more details

sudo nano /var/log/syslog

Other useful commands

Stop a service

sudo systemctl stop SERVICE_NAME

Restart a service

sudo systemctl restart SERVICE_NAME

Show process ID for process listening on port

fuser -n tcp 8080

Show all processes

sudo netstat -nlp

Kill process by pid

sudo kill <pid>

Source Code

Flask Api and Unit File

References

Understanding Systemd Units and Unit Files

Understanding Systemd