FastAPI Configuration & Deployment 🚀
The Restaurant Kitchen Analogy
Imagine you’re running a restaurant kitchen. Before customers arrive, you need to:
- Turn on the ovens and prep ingredients (startup)
- Know secret recipes and settings (environment variables)
- Have a recipe book with all the rules (Pydantic settings)
- Set up the serving window properly (server configuration)
- Pack everything in a food truck so you can cook anywhere! (Docker)
That’s exactly what FastAPI configuration and deployment is all about!
1. Lifespan Context Manager 🎬
What Is It?
Think of the lifespan like the opening and closing routine of your restaurant.
Opening time (startup):
- Turn on lights
- Heat up ovens
- Prep ingredients
Closing time (shutdown):
- Turn off ovens
- Clean everything
- Lock the doors
In FastAPI, the lifespan context manager handles what happens when your app starts and stops.
Why Do We Need It?
Some things take time to set up:
- Database connections (like warming up the oven)
- Loading AI models (like prepping special ingredients)
- Connecting to external services
You don’t want to do this for EVERY customer request!
Simple Example
from contextlib import asynccontextmanager
from fastapi import FastAPI
# This is like your kitchen's
# opening and closing checklist
@asynccontextmanager
async def lifespan(app: FastAPI):
# STARTUP: Kitchen opens!
print("Starting up...")
# Connect to database
# Load ML models
yield # App runs here
# SHUTDOWN: Kitchen closes!
print("Shutting down...")
# Close database
# Cleanup resources
app = FastAPI(lifespan=lifespan)
Real-World Example
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: Load expensive model
app.state.model = load_ml_model()
app.state.db = await connect_db()
yield
# Shutdown: Clean up
await app.state.db.close()
app = FastAPI(lifespan=lifespan)
Key Point: Everything BEFORE yield runs at startup. Everything AFTER runs at shutdown!
2. Environment Variables 🔐
What Are They?
Environment variables are like secret notes you pass to your kitchen staff.
Imagine writing:
- “Today’s special sauce recipe is X”
- “The WiFi password is Y”
- “The supplier’s phone number is Z”
You don’t put these in the recipe book (code). They change based on the day, location, or situation!
Why Use Them?
Security: Never put passwords in your code!
Flexibility: Same code works in different places:
- Your laptop (development)
- Test server (staging)
- Real server (production)
How to Access Them
import os
# Reading environment variables
database_url = os.getenv("DATABASE_URL")
api_key = os.getenv("API_KEY", "default")
debug = os.getenv("DEBUG", "false")
Setting Environment Variables
In your terminal:
export DATABASE_URL="postgresql://..."
export API_KEY="super-secret-key"
export DEBUG="true"
In a .env file:
DATABASE_URL=postgresql://localhost/mydb
API_KEY=super-secret-key
DEBUG=true
Important: Never commit .env files to git! Add them to .gitignore.
3. Pydantic Settings 📋
What Is It?
Pydantic Settings is like having a smart recipe book that:
- Knows what ingredients you need
- Checks if ingredients are correct
- Has default values if something is missing
Why Not Just Use os.getenv?
# The old way - lots of problems!
port = os.getenv("PORT") # Is this a string or int?
debug = os.getenv("DEBUG") # Is "true" == True?
Problems:
- Everything is a string
- No validation
- No defaults
- Easy to make typos
The Pydantic Way
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
api_key: str
debug: bool = False # Default value!
port: int = 8000 # Auto converts!
class Config:
env_file = ".env"
settings = Settings()
# Now you can use:
# settings.database_url (guaranteed string)
# settings.debug (guaranteed boolean)
# settings.port (guaranteed integer)
Magic Features
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
# Different name in env vs code
db_url: str = Field(
alias="DATABASE_URL"
)
# Nested settings with prefix
redis_host: str = "localhost"
redis_port: int = 6379
class Config:
env_file = ".env"
env_prefix = "APP_" # APP_REDIS_HOST
Using Settings in FastAPI
from functools import lru_cache
@lru_cache # Load settings only once!
def get_settings():
return Settings()
@app.get("/info")
def get_info(settings = Depends(get_settings)):
return {"debug": settings.debug}
4. Server Configuration ⚙️
What Is It?
Server configuration is like setting up your restaurant’s service window:
- How many customers can you serve at once?
- What’s your address?
- When are you open?
Uvicorn: Your Server
FastAPI doesn’t run by itself. It needs a server like Uvicorn.
# Basic run
uvicorn main:app
# With options
uvicorn main:app --host 0.0.0.0 --port 8000
Key Configuration Options
uvicorn main:app \
--host 0.0.0.0 \ # Accept all connections
--port 8000 \ # Port number
--workers 4 \ # Number of processes
--reload # Auto-reload on changes
Configuration in Code
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
workers=4,
reload=True,
log_level="info"
)
Production vs Development
| Setting | Development | Production |
|---|---|---|
reload |
True |
False |
workers |
1 |
4+ |
log_level |
debug |
warning |
host |
127.0.0.1 |
0.0.0.0 |
Gunicorn + Uvicorn (Production)
For production, use Gunicorn as a process manager:
gunicorn main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
-b 0.0.0.0:8000
5. Docker Containerization 📦
What Is Docker?
Remember our restaurant analogy? Docker is like a food truck that:
- Has everything packed inside
- Works the same everywhere
- Easy to move around
- Self-contained kitchen!
Why Docker?
Problem: “It works on my machine!”
Solution: Put your machine IN the container!
Docker packages:
- Your code
- Python
- All dependencies
- Configuration
The Dockerfile
A Dockerfile is like the blueprint for your food truck:
# Start with Python installed
FROM python:3.11-slim
# Set work directory (inside truck)
WORKDIR /app
# Copy requirements first (for caching)
COPY requirements.txt .
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy your code
COPY . .
# Tell Docker what port to use
EXPOSE 8000
# Start command
CMD ["uvicorn", "main:app", \
"--host", "0.0.0.0", \
"--port", "8000"]
Building and Running
# Build the image (create the truck)
docker build -t my-fastapi-app .
# Run the container (drive the truck)
docker run -p 8000:8000 my-fastapi-app
Docker Compose
For apps with multiple services (database, cache, etc.):
# docker-compose.yml
version: "3.8"
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://db/app
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=app
- POSTGRES_PASSWORD=secret
Run with:
docker-compose up
Best Practices
# Multi-stage build (smaller image)
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir \
--wheel-dir /wheels -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache /wheels/*
COPY . .
CMD ["uvicorn", "main:app", \
"--host", "0.0.0.0"]
Putting It All Together 🎯
Here’s how everything connects:
graph TD A["Your Code"] --> B["Pydantic Settings"] B --> C["Reads .env file"] B --> D["Environment Variables"] A --> E["Lifespan Manager"] E --> F["Startup Tasks"] E --> G["Shutdown Tasks"] A --> H["Uvicorn Server"] H --> I["Docker Container"] I --> J["Deployed App!"]
Complete Example
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
api_key: str
debug: bool = False
class Config:
env_file = ".env"
from contextlib import asynccontextmanager
from fastapi import FastAPI
from settings import Settings
settings = Settings()
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
print(f"Debug mode: {settings.debug}")
yield
# Shutdown
print("Goodbye!")
app = FastAPI(lifespan=lifespan)
@app.get("/")
def root():
return {"status": "running"}
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", \
"--host", "0.0.0.0"]
Quick Summary 📝
| Concept | Purpose | Like… |
|---|---|---|
| Lifespan | Startup/Shutdown | Opening/Closing routine |
| Env Vars | Secret config | Secret notes |
| Pydantic | Type-safe settings | Smart recipe book |
| Uvicorn | Server | Service window |
| Docker | Deployment | Food truck |
You Did It! 🎉
You now understand how to:
- Set up startup and shutdown routines
- Keep secrets safe with environment variables
- Use Pydantic for bulletproof settings
- Configure your server properly
- Package everything in Docker
Your FastAPI app is ready to serve the world! 🌍
