Deployment¶
Responder applications are standard ASGI apps. ASGI (Asynchronous Server Gateway Interface) is the modern successor to WSGI — it supports async, WebSockets, and HTTP/2. This means you can deploy a Responder app anywhere that runs Python, using any ASGI server.
Running Locally¶
During development, api.run() is all you need:
if __name__ == "__main__":
api.run()
This starts a uvicorn server on
127.0.0.1:5042. Uvicorn is a lightning-fast ASGI server built on
uvloop — it handles thousands of
concurrent connections efficiently and protects against slowloris attacks,
making a reverse proxy like nginx optional for many deployments.
Docker¶
Docker is the most common way to package and deploy web applications. Here’s a minimal Dockerfile:
FROM python:3.13-slim
WORKDIR /app
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY . .
RUN uv pip install --system responder
ENV PORT=80
EXPOSE 80
CMD ["python", "api.py"]
Build and run:
$ docker build -t myapi .
$ docker run -p 8000:80 myapi
The python:3.13-slim image is about 150MB — small enough for fast
deploys but includes everything you need. Using uv for installs
is significantly faster than pip. For even smaller images, you can use
python:3.13-alpine, though some packages may need extra build
dependencies.
Cloud Platforms¶
Responder automatically honors the PORT environment variable. When
PORT is set, the server binds to 0.0.0.0 on that port — this is
the convention that virtually every cloud platform uses.
This means zero configuration on:
Fly.io —
fly launchand you’re doneRailway — push your code, Railway sets
PORTRender — set start command to
python api.pyGoogle Cloud Run — containerize and deploy
Azure Container Apps — same pattern
AWS App Runner — and here too
The pattern is always the same: deploy your code, set the start command
to python api.py, and the platform handles the rest.
Health Check Endpoint¶
Every production deployment needs a health check — a lightweight endpoint that monitoring tools, load balancers, and orchestrators can poll to verify your service is running:
@api.route("/health")
def health(req, resp):
resp.media = {"status": "healthy"}
Keep it simple. Don’t query the database or do expensive work — the health check should return instantly. Cloud platforms, Docker, and Kubernetes all look for an HTTP 200 to confirm your service is alive.
For Docker, add a HEALTHCHECK instruction:
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/health || exit 1
Uvicorn Directly¶
For production deployments where you want more control, bypass
api.run() and use uvicorn directly:
$ uvicorn api:api --host 0.0.0.0 --port 8000 --workers 4
The --workers flag spawns multiple processes, each handling requests
independently. A good starting point is 2-4 workers per CPU core.
Uvicorn supports many options — SSL certificates, access logging, graceful shutdown timeouts, and more. See the uvicorn documentation for details.
For platforms like Heroku or Railway that use a Procfile:
web: uvicorn api:api --host 0.0.0.0 --port $PORT --workers 4
Docker Compose¶
For local development with databases and other services, Docker Compose ties everything together:
# docker-compose.yml
services:
api:
build: .
ports:
- "5042:80"
environment:
- PORT=80
- DATABASE_URL=postgresql+asyncpg://user:pass@db/myapp
- SECRET_KEY=dev-secret
depends_on:
- db
db:
image: docker.io/postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Run with docker compose up. The API waits for db to start, then
connects using the DATABASE_URL environment variable.
Reverse Proxy¶
For high-traffic production deployments, you may want a reverse proxy like nginx or Caddy in front of your application for:
SSL/TLS termination — let the proxy handle HTTPS certificates
Load balancing — distribute traffic across multiple app instances
Static asset serving — offload static files to the proxy
Rate limiting — at the infrastructure level
A minimal Caddy config that handles HTTPS automatically:
# Caddyfile
example.com {
reverse_proxy localhost:5042
}
Responder’s TrustedHostMiddleware and HTTPSRedirectMiddleware work
correctly behind proxies that set standard forwarding headers
(X-Forwarded-For, X-Forwarded-Proto).
That said, uvicorn is production-ready on its own. Many applications run uvicorn directly without a reverse proxy and do just fine.
Production Checklist¶
Before going live:
Set a secret key —
SECRET_KEYenv var, never the defaultDisable debug mode —
DEBUG=falseor omit it entirelySet allowed hosts — restrict to your actual domain names
Use multiple workers —
--workers 4or more, depending on CPU coresAdd a health check —
/healthendpoint for monitoringEnable HTTPS — via your proxy, cloud platform, or uvicorn’s
--ssl-*flagsSet up logging — uvicorn logs requests by default; pipe them to your log aggregator
Pin your dependencies — use a lock file or pinned requirements for reproducible deploys