Course topics

By WebNest Studio

Python Tutorial

Configuring and Deploying FastAPI Applications

An API only becomes useful once it runs somewhere others can reach it. This lesson covers the path from laptop to production: managing configuration with environment variables and pydantic-settings, running with the production server, using multiple worker processes, packaging the app in a Docker image, and the checklist of things to get right — HTTPS, secrets, logging, health checks and database migrations.

Configuration with pydantic-settings

Settings that differ between environments — database URL, secret keys, allowed origins, debug flags — belong in environment variables, not in code (the twelve-factor approach). pydantic-settings (pip install pydantic-settings) reads them into a typed, validated BaseSettings class, optionally from a .env file during development. Missing required settings fail fast at start-up, and wrong types are rejected. Never commit .env files containing real secrets.

Running in Production

fastapi run main.py (or uvicorn main:app --host 0.0.0.0 --port 8000) starts the production server without auto-reload. Add --workers 4 to run several processes and use several CPU cores; in Kubernetes, you usually run one process per container and scale the number of containers instead. Put the app behind a reverse proxy or load balancer (Nginx, Traefik, a cloud load balancer) that terminates HTTPS, and pass --proxy-headers so FastAPI sees the real client address and scheme.

Production Checklist

  • Secrets from environment variables or a secrets manager; debug off.
  • HTTPS everywhere; strict CORS origins.
  • Structured logging and a /health endpoint for load balancers and orchestrators.
  • Database migrations (Alembic) run as a deployment step, never create_all() on a live database.
  • Pinned dependency versions, a small non-root Docker image, and automated tests in CI.
  • Popular hosts: any VPS with Docker, Render, Railway, Fly.io, Google Cloud Run, AWS (ECS, App Runner, Lambda with Mangum) and Azure Container Apps.

Examples

Typed settings from environment variables

Python
# config.py      pip install pydantic-settings
import os
from functools import lru_cache
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")

    app_name: str = "Webnest API"
    debug: bool = False
    database_url: str = "sqlite:///app.db"
    secret_key: str = Field(min_length=32)                 # required: no default
    allowed_origins: list[str] = ["http://localhost:5173"]

@lru_cache                                                  # read the environment once
def get_settings() -> Settings:
    return Settings()

# Simulating the environment a server would provide:
os.environ["APP_SECRET_KEY"] = "x" * 40
os.environ["APP_DEBUG"] = "true"
os.environ["APP_ALLOWED_ORIGINS"] = '["https://webnest.example"]'

settings = get_settings()
print(settings.app_name, settings.debug, settings.database_url)
print(settings.allowed_origins, len(settings.secret_key))

os.environ["APP_SECRET_KEY"] = "too-short"
try:
    Settings()
except Exception as e:
    print(type(e).__name__, "-", e.errors()[0]["msg"])
Output
Webnest API True sqlite:///app.db
['https://webnest.example'] 40
ValidationError - String should have at least 32 characters

Production commands

Python
pip freeze > requirements.txt                 # or use uv / Poetry lock files

fastapi run app/main.py --port 8000           # production server, no reload
fastapi run app/main.py --workers 4           # four worker processes

# Equivalent with Uvicorn directly, behind a reverse proxy:
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 --proxy-headers

alembic upgrade head                          # apply database migrations before starting
Output
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started parent process [1]
INFO:     Started server process [8]
INFO:     Started server process [9]
INFO:     Started server process [10]
INFO:     Started server process [11]
INFO:     Application startup complete.

A Dockerfile for a FastAPI application

Python
# Dockerfile
FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /code

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app ./app
RUN useradd --create-home appuser
USER appuser

EXPOSE 8000
CMD ["fastapi", "run", "app/main.py", "--port", "8000", "--proxy-headers"]

# Build and run:
#   docker build -t webnest-api .
#   docker run -p 8000:8000 --env-file .env webnest-api
Output
Successfully tagged webnest-api:latest
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

Common Mistakes

  • Running fastapi dev / --reload in production.
  • Committing .env files or hard-coding secrets in the Docker image.
  • Running containers as root and shipping huge images with build tools included.
  • Creating tables with create_all() on a production database instead of running migrations.
  • Forgetting --proxy-headers behind a proxy, so generated URLs use http instead of https.

Key Points to Remember

  • Keep configuration in environment variables; pydantic-settings validates it.
  • fastapi run (or uvicorn) serves production traffic; --workers uses more CPU cores.
  • Terminate HTTPS at a reverse proxy or load balancer.
  • Docker packages the app and its dependencies into a reproducible image.
  • Add health checks, logging, migrations and CI tests to every deployment.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.