Web Server with Flask

Basic Flask Server to serve static html files

My use case is simple: Using my documention built with Sphinx as a content for my Web server.

Creating a basic Flask Application

This Application has one function that uses the User input to display some custom text.

from flask import Flask

app = Flask(__name__)

@app.route('/user/<name>')
def user(name):
    return '<h1>Hello, {}!</h1>'.format(name)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=80)

Note

To access it from any device on your local network, set host=’0.0.0.0’

Serving Static files

The method send_static_files is used to output the build of our sphinx project.

from flask import Flask

app = Flask(__name__, static_url_path='/', static_folder='_build/')

@app.route('/')
@app.route('/<path:path>')
def serve_sphinx_docs(path='index.html'):
    return app.send_static_file(path)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=80)

Run a production server

When running publicly rather than in development, you should not use the built-in development server (flask run). The development server is provided by Werkzeug for convenience, but is not designed to be particularly efficient, stable, or secure.

Instead, use a production WSGI server. For example, to use Waitress, first install it in the virtual environment:

$ pip install waitress

Creat another script to import waitress and your Flask App and use the serve method:

from waitress import serve
import flask_app

serve(flask_app.app, host='0.0.0.0', port=8080, url_scheme='https')

Note

Specifying the proper host, port and url_scheme is crucial.

Tip

To check which services are running use: sudo service –status-all

Create a service with systemd to run the server at startup

In order to automatically run the Web server at each startup, we can create a service with systemd:

source: https://linuxconfig.org/how-to-autostart-python-script-on-raspberry-pi

Note

All service files are located in /etc/systemd/system

user@pi:/etc/systemd/system $ ls
bluetooth.target.wants                      graphical.target.wants
cloudflared.service                         halt.target.wants
cloudflared-update.service                  jenkins.service.d
cloudflared-update.timer                    multi-user.target.wants
dbus-fi.w1.wpa_supplicant1.service          network-online.target.wants
dbus-org.bluez.service                      poweroff.target.wants
dbus-org.freedesktop.Avahi.service          printer.target.wants
dbus-org.freedesktop.ModemManager1.service  reboot.target.wants
dbus-org.freedesktop.nm-dispatcher.service  remote-fs.target.wants
dbus-org.freedesktop.timesync1.service      runwaitress.service
default.target                              sockets.target.wants
dev-serial1.device.wants                    sshd.service
display-manager.service                     sysinit.target.wants
getty.target.wants                          timers.target.wants
[email protected]                        wayvnc.service.wants
user@pi:/etc/systemd/system $ cat runwaitress.service
[Unit]
Description=Run production server with Waitress
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/home/user/virtEnv/Website/bin/python /home/user/myApp/waitress_server.py --serve-in-foreground


[Install]
WantedBy=multi-user.target

Apply the appropriate permissions to the file so that systemd can execute it. Afterwards, we will apply the proper permissions to the systemd timer file.

$ sudo chmod 744 myApp/waitress_server.py
$ sudo chmod 664 /etc/systemd/system/runwaitress.service

Next, enable the service unit:

$ sudo systemctl daemon-reload
$ sudo systemctl enable runwaitress.service

Common pitfalls

Issues encountered and how they have been fixed.

Python OSError: [Errno 98] Address already in use

The Python error “OSError: [Errno 98] Address already in use” occurs when you have another process running on the specified port.

You can resolve the error by using the SO_REUSEADDR attribute or by identifying and stopping the process before you start your server.

To get the process ID of the process that is running on the specified port, use the lsof command.

$ sudo lsof -i :8081
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
python  1931 root    6u  IPv4   9077      0t0  TCP *:tproxy (LISTEN)

The PID column contains the ID of the process that is running on the specified port.

You can stop the process by issuing the kill command.

$ sudo kill -9 <YOUR_PID>