Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. It began as a simple wrapper around Werkzeug and Jinja and has become one of the most popular Python web application frameworks.


All relevant legal information can be found here


The name of the application you are going to set up is called basic_flask. If you wish to use another name make sure to replace basic_flask in all of the following steps with the name of your choice.

Create application directory and files

[isabell@stardust ~]$ mkdir basic_flask
[isabell@stardust ~]$ mkdir basic_flask/templates
[isabell@stardust ~]$ mkdir basic_flask/static
[isabell@stardust ~]$

Create ~/basic_flask/app.py with the following content:

#!/usr/bin/env python3.6
import os
from flask import Flask
from flask import render_template

app = Flask(__name__)

def index():
  message = "Hello from project_name"
  return render_template('index.html', message=message)

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

Create an html template file ~/basic_flask/templates/index.html with the following content:

<!-- templates/index.html -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">

Create a css file ~/basic_flask/static/style.css with the following content:

h1 {
  color: blue;

Setup Python environment and install required packages

You definitely want to create an isolated Python environment. That way the required packages you are going to install with pip are encapsulated from your system’s Python installation. We are using the venv module to first create a local environment called ENV (or whatever name you prefer) that we activate with source. Once active, use the venv’s pip to install flask and its dependencies as well as a local version of uwsgi.

[isabell@stardust ~]$ cd basic_flask
[isabell@stardust basic_flask]$ python3 -m venv ENV
[isabell@stardust basic_flask]$ source ENV/bin/activate
(ENV) [isabell@stardust basic_flask]$ pip install flask uwsgi
(ENV) [isabell@stardust basic_flask]$

Once you’re done playing with it, you can deactivate the virtual environment:

(ENV) [isabell@stardust basic_flask]$ deactivate
[isabell@stardust basic_flask]$


Using Werkzeug for development

You can use Werkzeug which gets shipped with Flask to spin up a small development server. But be aware: Do not use it in a production deployment. For more info head to https://www.palletsprojects.com/p/werkzeug/.

Note that if you run your application under a path different from /, this approach does not work because the requests don’t match the configured routes and because the server does not set the SCRIPT_NAME variable. The proper fix is using a uWSGI deployment as we will do in the next step.

To start Werkzeug execute the following commands. This enables the virtual Python environment and loads app.py. Stop it by pressing Ctrl + C.

[isabell@stardust ~]$ cd basic_flask
[isabell@stardust basic_flask]$ source ENV/bin/activate
(ENV) [isabell@stardust basic_flask]$ python app.py
 ℹ * Serving Flask app "app" (lazy loading)
 ℹ * Environment: production
     WARNING: This is a development server. Do not use it in a production deployment.
     Use a production WSGI server instead.
 ℹ * Debug mode: on
 ℹ * Running on (Press CTRL+C to quit)
 ℹ * Restarting with stat
 ℹ * Debugger is active!
 ℹ * Debugger PIN: 000-000-000
[isabell@stardust basic_flask]$

Using uWSGI for production

A more suited approach to serve your application would be to use uWSGI. The uWSGI project aims at developing a full stack for building hosting services. For more info head to https://uwsgi-docs.readthedocs.io/en/latest/.

Create the ini file ~/basic_flask/uwsgi.ini with the following content:

module = app:app
pidfile = basic_flask.pid
master = true
processes = 1
http-socket = :1024
chmod-socket = 660
vacuum = true

If your application does not run under / but under, say, /your/path/, replace the module = ... line with:

mount = /your/path=app:app
manage-script-name = true

To serve your application via uWSGI execute the following commands. Stop it by pressing Ctrl + C.

[isabell@stardust ~]$ cd basic_flask
[isabell@stardust basic_flask]$ source ENV/bin/activate
[isabell@stardust basic_flask]$ uwsgi uwsgi.ini
ℹ [uWSGI] getting INI configuration from uwsgi.ini
ℹ *** Starting uWSGI 2.0.20 (64bit) on [Tue Jan 21 15:47:41 2020] ***
ℹ ...
ℹ *** uWSGI is running in multiple interpreter mode ***
ℹ spawned uWSGI master process (pid: 23422)
ℹ spawned uWSGI worker 1 (pid: 23455, cores: 1)
[isabell@stardust basic_flask]$ ^C
[isabell@stardust basic_flask]$

Setup daemon

When serving a website with Flask, we want to have uWSGI running all the time. To do this, uberspace uses supervisord to start and restart processes. Create ~/etc/services.d/flask.ini with the following content:

command=%(ENV_HOME)s/basic_flask/ENV/bin/uwsgi uwsgi.ini

Now let’s start the service:

After creating the configuration, tell supervisord to refresh its configuration and start the service:

[isabell@stardust ~]$ supervisorctl reread
SERVICE: available
[isabell@stardust ~]$ supervisorctl update
SERVICE: added process group
[isabell@stardust ~]$ supervisorctl status
SERVICE                            RUNNING   pid 26020, uptime 0:03:14
[isabell@stardust ~]$

Setup nginx


Flask is running on port 1024.

Flask is now running on the server, but because of Uberspace’s network infrastructure can not yet be accessed from the web.

To make the application accessible from the outside, configure a web backend:

[isabell@stardust ~]$ uberspace web backend set / --http --port <port>
Set backend for / to port <port>; please make sure something is listening!
You can always check the status of your backend using "uberspace web backend list".
[isabell@stardust ~]$

Best Practices

uWSGI can be configured extensively and has its own page on best practices. Further recommendations can also be found in this write-up from EuroPython 2019.

Tested with Uberspace 7.11.5 and Flask 2.0.0 on Python 3.6.

Written by: Benjamin Wießneth <bwiessneth@gmail.com>, Christian Macht <https://github.com/cmacht>