Backend Development with FastAPI
Back to Build Real APIs

Build 6 — Deploy to Production

Deploy a previous build to Fly.io with Docker, managed Postgres, secrets, and migrations.

114 minutesProject submission
🎯 Free Guest Mode: You are learning for free. Sign in to save your completion progress and quiz answers.

Assignment Instructions

The brief

Take a previous build (Notes API, URL shortener, etc.) and deploy it to a real cloud platform with proper config, migrations, HTTPS, and a custom domain.

Step 1: Pick a platform

For this walkthrough: Fly.io (free tier, fast, full control).

Alternatives:

  • Railway (simpler).
  • Render (Heroku-like).
  • Northflank, Koyeb (other Heroku-alternatives).

Or: AWS Fargate, GCP Cloud Run for more control.

Step 2: Dockerize (15 min)

# Dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY src ./src
COPY alembic alembic
COPY alembic.ini ./

ENV PYTHONUNBUFFERED=1
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000

CMD ["sh", "-c", "alembic upgrade head && uvicorn notes_api.main:app --host 0.0.0.0 --port 8000 --workers 4"]

Test locally:

docker build -t my-api .
docker run -p 8000:8000 -e DATABASE_URL=sqlite:///./test.db my-api

Step 3: Fly.io setup (15 min)

brew install flyctl
fly auth signup    # or login
fly launch         # interactive setup

Generates fly.toml:

app = "my-notes-api"
primary_region = "bom"   # Mumbai

[build]
  dockerfile = "Dockerfile"

[env]
  LOG_LEVEL = "INFO"

[http_service]
  internal_port = 8000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0    # for free tier; scale up for prod

[[services.tcp_checks]]
  grace_period = "5s"
  interval = "15s"
  timeout = "2s"

Step 4: Provision a Postgres database (10 min)

fly postgres create --name my-notes-db --region bom
fly postgres attach --app my-notes-api my-notes-db

Auto-sets DATABASE_URL secret on your app.

Alternative: Supabase or Neon for managed Postgres outside Fly.

Step 5: Set secrets (5 min)

fly secrets set \
  SECRET_KEY=$(openssl rand -hex 32) \
  LOG_LEVEL=INFO \
  --app my-notes-api

NEVER commit these. fly.io stores them encrypted.

Step 6: Deploy (10 min)

fly deploy

Logs to terminal. After deploy:

fly status
fly logs
fly open    # opens in browser

Access:

Step 7: Migrations (5 min)

The Dockerfile runs alembic upgrade head at start. For separate migration runs:

fly ssh console
alembic upgrade head

Or run as a release command in fly.toml:

[deploy]
  release_command = "alembic upgrade head"

Step 8: Custom domain (5 min)

fly certs add api.mydomain.com
# Add CNAME at your DNS provider pointing to <app>.fly.dev
# Fly auto-provisions Let's Encrypt cert

After DNS propagates:

fly certs check api.mydomain.com

Step 9: Setup CI for auto-deploy (15 min)

.github/workflows/deploy.yml:

name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install pytest httpx
      - run: pytest
      - name: Deploy to Fly
        run: curl -L https://fly.io/install.sh | sh && /home/runner/.fly/bin/flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Generate Fly token:

fly auth token

Add as GitHub secret FLY_API_TOKEN.

Now every push to main: tests run; if green, deploy.

Step 10: Monitoring (10 min)

fly logs        # tail logs
fly status      # check instance health
fly metrics     # view dashboards

For richer observability:

  • Add Sentry for error tracking.
  • Ship logs to Loki / Datadog.
  • Add OpenTelemetry for traces.

Sentry setup:

import sentry_sdk
sentry_sdk.init(dsn=settings.sentry_dsn, traces_sample_rate=0.1)

Deliverables

  • Live URL of your deployed API.
  • Working /docs endpoint.
  • HTTPS with valid cert.
  • Database persistence.
  • CI auto-deploy on push to main.
  • A DEPLOY.md documenting the steps.

What you'll learn

  • Multi-stage Dockerfile for slim production images.
  • Cloud platform basics (Fly.io as example).
  • Managed Postgres provisioning.
  • Secrets management.
  • Custom domains + HTTPS via Let's Encrypt.
  • CI/CD pipeline that tests + deploys.

Stretch

  • Deploy to multiple regions (Fly auto-supports).
  • Add a CDN (Cloudflare in front).
  • Set up Sentry for error tracking.
  • Add metrics dashboard (Grafana + Prometheus).
  • Configure auto-scaling based on traffic.

📊 100-Point Evaluation Rubric (Case 6)

Evaluation DimensionWeightTarget Standard (Pass ≥ 80%)
1. Live Deployment40 ptsPublic HTTPS URL serving the API; /docs reachable; health endpoint passes from the public internet.
2. Data + Migrations30 ptsManaged Postgres attached; alembic upgrade head applied (release command or console); no create_all in production.
3. Config + Secrets15 ptsSecrets via platform secret store (never committed); multi-stage/slim Dockerfile; env-based config.
4. Operability15 ptsREADME documents deploy/redeploy/rollback steps; logs retrievable (fly logs or equivalent) with request tracing.

📤 Submission Instructions & Guidelines

  1. Deliverable Formats:
    • Option A (Recommended): Upload a single .zip archive containing your project directory: src/, tests/, Dockerfile (where applicable), and README.md with run instructions.
    • Option B: Upload your README.md and include your GitHub repository link in the Submission Notes box below.
  2. Pre-Submission Verification:
    • Run your full test suite (pytest) green; include the passing output in your README.
    • Exercise every endpoint once via /docs (Swagger UI) and paste one sample request/response per endpoint into your README.
    • Confirm no secrets are committed (.env git-ignored; secrets via environment only).
Checking your access…