Min 29-30 — Deployment 🚀¶
Heaven is an ASGI application, so anything that serves ASGI serves Heaven: uvicorn, hypercorn, granian, daphne.
The pre-flight checklist¶
Run through this before the first public request:
-
debugleft off (the default), so tracebacks are not served to clients -
SECRET_KEYread from the environment, never committed - Static files served by your proxy, not
app.ASSETS()(why) -
App(max_body_size=...)set, and a matching cap in the proxy -
--no-reload - Security headers (copy-paste hook)
Running it¶
heaven run main:app --host 0.0.0.0 --port 8000 --no-reload
A thin wrapper over uvicorn. Fine for a container that already has a process manager around it.
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
Gunicorn's process supervision — restarting workers that die or leak — is what you want on a long-running host.
app.listen() is for local runs
Router.listen() starts uvicorn in-process, which is fine for development. On a real host prefer the CLI or a supervised uvicorn/gunicorn command so you get process management.
Workers multiply your daemons
Each worker process runs its own copy of every daemon. With --workers 4, a cleanup daemon runs four times on every tick. Either run daemons in a single dedicated process, or make them idempotent and safe to race.
Docker¶
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PYTHONUNBUFFERED=1
CMD ["heaven", "run", "main:app", "--host", "0.0.0.0", "--port", "8000", "--no-reload"]
Behind a proxy¶
Put Nginx or Caddy in front for TLS, static files, and body limits:
server {
listen 443 ssl http2;
server_name example.com;
client_max_body_size 10M; # belt and braces with App(max_body_size=...)
location /static/ {
alias /var/www/app/assets/; # faster, and avoids app.ASSETS()
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1; # websockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
There's a fuller treatment — security headers, CSP, secrets — in Going to Production.
Next: You made it → Mastery