Deploying a Python web application is extremely complicated. Is there a simpler way?
Why properly deploying a Python web app requires five additional applications (e.g., nginx, uwsgi, certbot, cron, systemd).
Flask makes it easy to write a simple web service using Python. But making the service available to others is extremely complicated. Five additional applications require configuration: a WSGI http server, a reverse proxy, a TLS certificate (renewal) service, cron
, and an init system (aka a process manager). Worse, you cannot write your application in the manner you would write any other Python application (e.g., python3 app.py
). You must write your Flask application in such a way that it will “play well” with these applications (you must use flask run
). This post describes each of the five additional components. A future post will explore how to simplify the setup. (Spoiler: you can reduce the number of additional applications.)
The first additional component is a WSGI (/ˈ(h)wiskē/) http server. The Flask documentation calls these “Standalone WSGI containers”. Examples of such servers are Gunicorn, uWSGI, and Werkzeug (aka Flask’s “Development Server”). A WSGI http server is needed because without it, Flask cannot respond to more than one HTTP request at a time (due to Python’s running in a single thread). Being able to respond to multiple HTTP requests simultaneously is essential because most visitors to a web page require responses to multiple requests. For example, a user trying to load a page which contains four images and two CSS files needs responses to seven requests in order to view the page (six plus one for the initial HTML page). These seven requests will be made within milliseconds of each other. A WSGI http server allows the Flask application to respond to these requests at the same time.
The second additional component is a reverse proxy. A reverse proxy allows your Python web application to use TLS. And everyone uses TLS these days. Because WSGI http servers tend not to support TLS, a reverse proxy is needed. (In the rare case that WSGI software does support TLS, it tends not to support the most recent and secure version of TLS.) Reverse proxies provide other benefits, such as load balancing and compressing static files. The documentation for Waitress, WSGI software mentioned in the Flask documentation, describes why a reverse proxy is paired with a WSGI server:
Often people will set up “pure Python” web servers behind reverse proxies, especially if they need TLS support (Waitress does not natively support TLS). Even if you don’t need TLS support, it’s not uncommon to see Waitress and other pure-Python web servers set up to only handle requests behind a reverse proxy; these proxies often have lots of useful deployment knobs.
The third and fourth additional components, a TLS certificate renewal service and cron, come as a pair. (TLS is the protocol which allows you to serve your website securely using HTTPS.) Because TLS certificates expire after 90 days, they must periodically be renewed. Software such as certbot or acme.sh does the renewing. cron is used to run the software once a day. When the software runs, it renews any certificates which need renewing. These two components are typically configured at the same time as the reverse proxy because the location of the TLS certificate must be included in the reverse proxy’s configuration.
The fifth additional component is the init system (aka process manager). This component makes sure that the web application starts when the host computer starts. Because computers are rebooted occasionally—due to kernel upgrades, power outages, etc.—this component is essential. Configuring the init system—typically systemd or OpenRC—to launch software on boot is not difficult. And it’s typically something a Linux user already knows how to do because many pieces of software use the init system. We also never need to bother installing the init system. Like cron, it is always already installed on the computer.
It does not have to be this complicated. Making a simple web service available on the internet need not require so many additional pieces of software. And it need not require us to write our code in a different way than we normally would. We know this to be the case because we observe examples of web services written in other languages such as Go which require far less configuration. Making Python web service deployment simpler is a goal worth pursuing. At a minimum, it would save programmers time that could be better spent elsewhere.
In a future post, we will consider a way to write a Python application which requires fewer additional components.
This is episode 1 of season 1 of Polyglot Python. This is version 1, published on 2021-06-11. The first version appeared 2021-06-11.
If you read this episode and value it, consider subscribing to the paid version. Doing so supports the development of new episodes. There are a couple of subscriber-only perks.
Subscriptions are not intended to discriminate against those lacking access to financial resources. If you have limited access to money, I’ll give you a free subscription.
If you’re reading this in your inbox, know that there’s a version on the web.