Template
Python Production Stack
Python, Nginx, Gunicorn and a hardened systemd service for one FastAPI, Django or Flask app.
Steps, in order · version 1.0
Options
As they appear on the run form, with their defaults.
Application name
Text
Lowercase letters, digits and hyphens, such as api. Names the folder, account and service.
Default: Required
Application module
Text
What Gunicorn loads, such as config.wsgi:application for Django or app.main:app for FastAPI.
Default: Required
Application interface
One choice
- ASGI: FastAPI, Starlette, Django over ASGI
- WSGI: Django, Flask
Default: ASGI: FastAPI, Starlette, Django over ASGI
Python
One choice
On Ubuntu and Debian, both choices install the release's one Python 3.
- Newest Python 3 in the distribution's repositories
- The distribution's default python3
Default: Newest Python 3 in the distribution's repositories
Port on 127.0.0.1
Text
Gunicorn listens here, on this server only.
Default: 8000
Python tools
Any of the choices
- uv
- Poetry
- Pipenv
Default: uv
Client headers
Any of the choices
For database drivers and libraries that compile against them.
- PostgreSQL (psycopg)
- MySQL and MariaDB (mysqlclient)
- libxml2 and libxslt (lxml)
- JPEG and zlib (Pillow)
Default: None
What you get
Everything one Python web application needs on the server except its code: Python with venv and pip, Nginx, Gunicorn in the application's own virtual environment (with Uvicorn workers for ASGI), the Python tools you choose with a compiler and build headers, a service account, Gunicorn settings and a hardened systemd service on 127.0.0.1. The service is installed but not started until you deploy.
Choosing the options
Application name — lowercase letters, digits and hyphens, such as api. It names /srv/api, the api account and the api service.
Application module — what Gunicorn loads: app.main:app for a FastAPI app in app/main.py; config.asgi:application or config.wsgi:application for Django, where config holds settings.py; app:app for Flask in app.py.
Application interface — ASGI for FastAPI, Starlette and Django over ASGI; WSGI for Django and Flask.
Python — Newest Python 3 on the RHEL family installs the newest Python packaged beside the system one, such as 3.12; on Ubuntu and Debian both choices install the release's Python 3.
Port on 127.0.0.1 — where Gunicorn listens, on this server only. 8000 unless something else uses it.
Python tools — uv, Poetry or Pipenv, installed with pipx and available to every account.
Client headers — for dependencies that compile against them: PostgreSQL for psycopg, MySQL and MariaDB for mysqlclient, libxml2 for lxml, JPEG and zlib for Pillow.
What happens
- Update Packages.
- Install Python.
- Install Nginx.
- Install Gunicorn into the application's own virtual environment,
/srv/api/venvforapi. - Install Python Tools, with a compiler and build headers.
- Configure Python App: the account, the Gunicorn settings (
/etc/api/gunicorn.conf.pyforapi) and the service.
After the run
For an application named api on port 8000:
- Deploy the code to
/srv/api/current. - Install its requirements:
/srv/api/venv/bin/pip install -r /srv/api/current/requirements.txt, or with the tool you chose, for uvuv pip install --python /srv/api/venv/bin/python -r requirements.txt. - Put its settings in
/etc/api/environment, owned by root, mode 0640, groupapi. - Start it:
systemctl enable --now api, and checkcurl http://127.0.0.1:8000/. - Put HTTPS in front with Configure Reverse Proxy or the Nginx Reverse Proxy + Auto-Renewing SSL template, forwarding to
http://127.0.0.1:8000.
Common problems
- The port is already in use, or the name is taken by an existing account or service — see Configure Python App.
- The Uvicorn worker does not import — see Install Gunicorn.
- A header package is not available on the RHEL family — enable CodeReady Builder; see Install Python Tools.
- The service starts and stops once you enable it — read
journalctl -u api -n 50; the module path and missing settings are the usual causes.