Missing Guide: Quart deployment

Reading Time: 6 min / Published: 3/21/2020 /

Quart is a Python ASGI web microframework. It is intended to provide the easiest way to use asyncio functionality in a web context with python.

Quart is an up and coming python microframeork that is already production ready.

To deploy Quart you cannot rely on traditional python WSGI servers like apache's mod wsgi or uwsgi. Instead you need to rely on ASGI servers such as Hypercorn, Uvicorn or Gunicorn. All commands mentioned in this guide are run on Debian buster (10) with the latest updates. The commands were tested on ubuntu too. Your commands may vary for e.g., user creation.

But those servers shouldn't be directly exposed to the user for their lack of e.g., load balancing … For these functions nginx or apache2 are good and reliable options.

In this guide we will cover apache2 and nginx with reverse proxy configurations. The used ASGI server is Hypercorn due to its support for features such as HTTP/2.

We will also create a systemd service for managing our application. For the sake of this guide we assume you operate on a systemd based system.

To start with a simple project we will use a simple quart app.

app.py
1from quart import Quart
2
3app = Quart(__name__)
4
5@app.route("/")
6async def index():
7 return "Hello World"
8
9if __name__ == "__main__":
10 app.run()

If you run this app you will receive a simple text response on "http://127.0.0.1/". But this example runs on the integrated development ASGI server of Quart. To run this application with hypercorn.

Just execute python3 -m hypercorn app:app and hypercorn will run your application. Easy, right? Now we can begin with setting up an environment or you application. Begin with creating a new user (Guide user: hypercorn-executor).

You may want to disable ssh access for this user.

sudo adduser hypercorn-executor

Now you can start moving the project files to a dedicated folder. For this guide we will use /var/www/your-project. We're now ready to create the systemd service unit.

/etc/systemd/system/<your-service-name>.service
1[Unit]
2Description=<your project> hypercorn service
3After=postgresql.service
4StartLimitIntervalSec=0
5
6[Service]
7Type=simple
8Restart=always
9RestartSec=1
10User=<your dedicated user>
11WorkingDirectory=<your dedicated project directory>
12ExecStart=python3 -m hypercorn app:app
13
14[Install]
15WantedBy=multi-user.target

You may adjust this service unit to e.g., be executed after another service your depending upon such as mysql.service. Make sure to set the file permissions on your dedicated project directory to your new user. You replace hypercorn at this point with another ASGI server when required.

sudo chown -R <your dedicated user>:<your dedicated user> <your dedicated project directory>

You should at this point log into your dedicated user:

su <your-dedicated-user>

And install your dependencies and hypercorn:

python3 -m pip install hypercorn quart

Now make sure your app is still executable:

python3 -m hypercorn app:app

Now logout and reload the systemd daemon:

systemctl daemon-reload

And start your service:

sudo service <your-service-name> start

And your service is up and running. When something goes wrong e.g. a missing dependency see systemctl status <your-service-name>.service. Now you can tray adjusting the hypercorn options (worker, loop …) to your system.

Check if your service can start if not move it away from /etc/systemd. It can cause boot failures when not configure properly

You can finally setup the webserver of your choice (nginx or apache) with a reverse proxy config.

nginx example based on Gunicorn Documentation:

quart-site.conf
1worker_processes 1;
2
3user nobody nogroup;
4# 'user nobody nobody;' for systems with 'nobody' as a group instead
5error_log /var/log/nginx/error.log warn;
6pid /var/run/nginx.pid;
7
8events {
9 worker_connections 1024; # increase if you have lots of clients
10 accept_mutex off; # set to 'on' if nginx worker_processes > 1
11 # 'use epoll;' to enable for Linux 2.6+
12 # 'use kqueue;' to enable for FreeBSD, OSX
13}
14
15http {
16 include mime.types;
17 # fallback in case we can't determine a type
18 default_type application/octet-stream;
19 access_log /var/log/nginx/access.log combined;
20 sendfile on;
21
22 upstream app_server {
23 # fail_timeout=0 means we always retry an upstream even if it failed
24 # to return a good HTTP response
25
26 # for UNIX domain socket setups
27 server unix:/tmp/gunicorn.sock fail_timeout=0;
28
29 # for a TCP configuration
30 # server 192.168.0.7:8000 fail_timeout=0;
31 }
32
33 server {
34 # if no Host match, close the connection to prevent host spoofing
35 listen 80 default_server;
36 return 444;
37 }
38
39 server {
40 # use 'listen 80 deferred;' for Linux
41 # use 'listen 80 accept_filter=httpready;' for FreeBSD
42 listen 80;
43 client_max_body_size 4G;
44
45 # set the correct host(s) for your site
46 server_name example.com www.example.com;
47 server_tokens off; # Best practice
48
49 keepalive_timeout 5;
50
51 # path for static files
52 root <your dedicated project folder static folder>;
53
54 location / {
55 # checks for static file, if not found proxy to app
56 try_files $uri @proxy_to_app;
57 }
58
59 location @proxy_to_app {
60 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
61 proxy_set_header X-Forwarded-Proto $scheme;
62 proxy_set_header Host $http_host;
63 # we don't want nginx trying to do something clever with
64 # redirects, we set the Host: header above already.
65 proxy_redirect off;
66 proxy_pass http://127.0.0.1:8000;
67 # Port may vary with hypercorn configuration
68 }
69
70 error_page 500 502 503 504 /500.html;
71 location = /500.html {
72 root <your dedicated project folder public folder>;
73 }
74 }
75}

You may want to disable access logging for privacy and performance reasons

Apache2 Reverse Proxy:

quart-site.conf
1<VirtualHost *:443>
2 Servername example.org
3
4 # Reverse Proxy
5 ProxyRequests Off
6 <Proxy *>
7 AddDefaultCharset Off
8 Order deny,allow
9 Allow from all
10 </Proxy>
11 ProxyPreserveHost On
12 ProxyPass / http://127.0.0.1:8000/
13 ProxyPassReverse / http://127.0.0.1:8000/
14 # May vary depending on hypercorn options
15
16 # Add 500 fallback
17 ErrorDocument 500 <your dedicated project folder>/500.html
18</VirtualHost>

You're now ready to rock. For more information take a look at my references.

My References: