Examples
Short, copy-pasteable recipes. Install with PyPI or any other method, then run caddy run --config Caddyfile (or caddysnake with equivalent flags).
FastAPI (ASGI)
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.post("/items/")
def create_item(item: Item):
return item
http://localhost:9080 {
python /* {
module_asgi "main:app"
lifespan on
venv "./venv"
}
}
python -m venv venv && source venv/bin/activate
pip install fastapi
caddy run --config Caddyfile
Flask (WSGI)
# main.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def hello():
return jsonify({"message": "Hello, World!"})
http://localhost:9080 {
python /* {
module_wsgi "main:app"
workers 4
venv "./venv"
}
}
Django (WSGI)
http://localhost:9080 {
python /* {
module_wsgi "mysite.wsgi:application"
working_dir "."
venv "./venv"
}
}
pip install django
django-admin startproject mysite .
python manage.py migrate
caddy run --config Caddyfile
For Django Channels / ASGI, swap to module_asgi "mysite.asgi:application".
Socket.IO
# main.py
from fastapi import FastAPI
from socketio import AsyncServer
from socketio.asgi import ASGIApp
app = FastAPI()
sio = AsyncServer(async_mode="asgi")
app.mount("/", ASGIApp(sio))
@sio.event
async def message(sid, data):
await sio.emit("message", data, skip_sid=sid)
http://localhost:9080 {
python /* {
module_asgi "main:app"
lifespan on
venv "./venv"
}
}
Environment variables
python {
module_asgi "shop:app"
working_dir "/apps/shop"
env_file "/apps/shop/.env"
env_var APP_NAME "shop"
}
env_var overrides env_file for the same key. Placeholders work in values for dynamic apps.
Autoreload
python /* {
module_wsgi "main:app"
venv "./venv"
autoreload
}
Edits to .py files reload the app in place (500ms debounce). Syntax errors → HTTP 503 until fixed.
Avoid autoreload behind long-lived production WebSockets; see Architecture.
Multi-tenant / branch hosts
project/
├── app1/app1.py
├── app2/app2.py
└── Caddyfile
*.127.0.0.1.nip.io:9080 {
python /* {
module_asgi "{http.request.host.labels.6}:app"
working_dir "{http.request.host.labels.6}/"
autoreload
}
}
curl http://app1.127.0.0.1.nip.io:9080/
curl http://app2.127.0.0.1.nip.io:9080/
Each hostname resolves its own app on first request (cached; default max 128). For real HTTPS on a VPS IP via nip.io, use on-demand TLS + permission python_dir — see the reference.
For a production-shaped preview setup (release dirs + DB clones), read Branch previews on one box.