Skip to content

Python ​

zbpack builds Python apps that have a requirements.txt, pyproject.toml (Poetry, uv, PDM) or Pipfile. It detects FastAPI, Django and Flask and picks a production server for each.

FrameworkStart command zbpack uses
FastAPIuvicorn main:app --host 0.0.0.0 --port 8080
Djangogunicorn --bind :8080 <project>.wsgi
Flaskgunicorn --bind :8080 main:app

The Python version defaults to 3.13; set another in .python-version, pyproject.toml or with ZBPACK_PYTHON_VERSION. To start the app differently, set ZBPACK_START_COMMAND under Settings → Build → zbpack options.

FastAPI ​

python
# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {"message": "Hello from FastAPI"}
txt
# requirements.txt
fastapi
uvicorn[standard]

zbpack takes the first of main.py, app.py, server.py and app/__init__.py that exists, and the variable assigned FastAPI(...) in it (app above). With another layout, such as app/main.py, set the start command:

sh
ZBPACK_START_COMMAND=uvicorn app.main:app --host 0.0.0.0 --port 8080

Django ​

The project needs manage.py and gunicorn in requirements.txt:

txt
django
gunicorn

Read production settings from variables in settings.py:

python
import os

SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "")
DEBUG = os.environ.get("DJANGO_DEBUG") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
CSRF_TRUSTED_ORIGINS = os.environ.get("DJANGO_CSRF_TRUSTED_ORIGINS", "").split(",")

and set them on the Variable tab:

sh
DJANGO_SECRET_KEY=${PASSWORD}
DJANGO_ALLOWED_HOSTS=${ZENAVO_WEB_DOMAIN}
DJANGO_CSRF_TRUSTED_ORIGINS=${ZENAVO_WEB_URL}

(WEB is the name of the service's port; use the name shown on its Networking tab.)

Static files ​

When settings.py sets both STATIC_URL and STATIC_ROOT, zbpack runs python manage.py collectstatic --noinput during the build and serves the files with nginx in front of gunicorn, or leaves them to WhiteNoise when its middleware is in settings.py:

python
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

The build has none of the service's variables, so settings.py must load without them: read DJANGO_SECRET_KEY with os.environ.get(...) as above, not os.environ[...].

Migrations ​

Migrations need the database, which the build cannot reach, so run them at start:

sh
ZBPACK_START_COMMAND=python manage.py migrate --noinput && gunicorn --bind :8080 mysite.wsgi

Connect a database ​

Add PostgreSQL to the project and use its connection string, for example with dj-database-url or SQLAlchemy:

sh
DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}
python
import dj_database_url

DATABASES = {"default": dj_database_url.config(conn_max_age=600)}

Install a driver too: psycopg[binary] for PostgreSQL, mysqlclient or PyMySQL for MySQL and MariaDB.

Common problems ​

SymptomCause
ModuleNotFoundError at startThe package is missing from requirements.txt (or pyproject.toml).
Django answers 400 Bad RequestThe domain is not in ALLOWED_HOSTS.
Django forms fail with CSRF verification failedThe https:// address is not in CSRF_TRUSTED_ORIGINS.
FastAPI deploy never becomes healthyThe app object is not at main:app. Set ZBPACK_START_COMMAND.
Workers are killed under loadThe service's memory is too small for the number of gunicorn workers. Lower --workers, or give the service more memory.

Not affiliated with Zeabur.