DeployRuntimes

Python

Python apps are WSGI — agenthost imports the name app from app.py and serves it with gunicorn.

Python apps are served as a WSGI application. agenthost imports the name app from app.py — so your project must have a file named app.py at its root that exposes a WSGI callable named app. This is a fixed convention, not something you can rename with a parameter.

  • The entrypoint is app in app.py. A Flask or Django app object named app in that file is exactly what's expected.
  • List your dependencies in requirements.txt, and include gunicorn there — that's what serves the app. Omit it and the app fails to start.
app.py
# must expose `app`
from flask import Flask
app = Flask(__name__)

@app.route("/health")
def health():
    return {"ok": True}
requirements.txt
flask
gunicorn

The most common Python failure

The entrypoint isn't named app.py, or the WSGI object inside it isn't named app — so the import fails and the container crash-loops even though provisioning "succeeded". If you need a different framework or an async (ASGI) server, adapt it to expose a WSGI app in app.py, or wrap it so that name resolves.

Supported versions are 3.11, 3.12 and 3.13, defaulting to 3.12 — see Choosing a version.

Making the name resolve

Your code doesn't have to live in app.py; the name just has to be importable from there.

app.py — a Django project
from myproject.wsgi import application as app
app.py — an ASGI framework, wrapped
# FastAPI is ASGI; gunicorn here is WSGI. The a2wsgi adapter bridges it.
from a2wsgi import ASGIMiddleware
from main import app as asgi_app

app = ASGIMiddleware(asgi_app)

The wrapper approach works, but if you need real ASGI concurrency, bring your own Dockerfile and run uvicorn yourself on port 8000.

What gets built

FROM python:<version>-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["sh", "-c", "gunicorn app:app -b 0.0.0.0:8000"]

requirements.txt is copied and installed before the rest of your source, so unchanged dependencies are cached between deploys. Traffic is routed to port 8000; gunicorn is already bound there for you.

Common failures

SymptomCause
"gunicorn: not found"gunicorn missing from requirements.txt
ModuleNotFoundError: No module named 'app'No app.py at the project root
Failed to find attribute 'app' in 'app'app.py exists but the callable inside is named something else
A package fails to build during installIt needs system libraries the slim image lacks — pin a wheel-shipping version or bring your own Dockerfile
Django: static files missingRun collectstatic before deploying, and serve them via WhiteNoise