Category: devops
Skip the tutorials that break in production. Here's how to set up FastAPI properly from day one.
Most FastAPI tutorials get you running in 5 minutes. Then you spend 5 hours figuring out why it breaks in production.
We've deployed dozens of FastAPI servers. Here's what actually works.
The setup that doesn't lie to you
First, forget uvicorn main:app --reload for anything beyond your laptop. Here's the production-ready structure:
your-api/
โโโ app/
โ โโโ __init__.py
โ โโโ main.py
โ โโโ models/
โ โโโ routers/
โ โโโ dependencies.py
โโโ requirements.txt
โโโ docker-compose.yml
โโโ gunicorn.conf.py
gunicorn.conf.py is the real MVP
This file is where tutorials fail you:
bind = "0.0.0.0:8000"
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
max_requests = 1000
max_requests_jitter = 50
preload_app = True
timeout = 30
keepalive = 2
Why these settings matter:
max_requestsprevents memory leaks from killing your serverpreload_appsaves memory when you scale workerstimeout = 30because your database queries will sometimes be slow
Environment variables that don't suck
Use Pydantic's BaseSettings. It's not optional:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
class Config:
env_file = ".env"
Docker that works
Multi-stage builds aren't just for show-offs:
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
CMD ["gunicorn", "app.main:app"]
The part nobody talks about
Health checks. Your container orchestrator needs to know if your app is actually working:
@app.get("/health")
def health_check():
return {"status": "healthy", "timestamp": datetime.utcnow()}
Add this to your Docker Compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
What we learned the hard way
After deploying FastAPI in production for 3 years:
CORS will bite you. Configure it early, not when your frontend team starts swearing.
Database connections leak. Use connection pooling from day one.
Logs matter. Structure them. Your future debugging self will thank you.
Version your API. /v1/ in the URL saves relationships later.
Skip the pain. Start with this setup.