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.mddocumenting 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 Dimension | Weight | Target Standard (Pass ≥ 80%) |
|---|---|---|
| 1. Live Deployment | 40 pts | Public HTTPS URL serving the API; /docs reachable; health endpoint passes from the public internet. |
| 2. Data + Migrations | 30 pts | Managed Postgres attached; alembic upgrade head applied (release command or console); no create_all in production. |
| 3. Config + Secrets | 15 pts | Secrets via platform secret store (never committed); multi-stage/slim Dockerfile; env-based config. |
| 4. Operability | 15 pts | README documents deploy/redeploy/rollback steps; logs retrievable (fly logs or equivalent) with request tracing. |
📤 Submission Instructions & Guidelines
- Deliverable Formats:
- Option A (Recommended): Upload a single
.ziparchive containing your project directory:src/,tests/,Dockerfile(where applicable), andREADME.mdwith run instructions. - Option B: Upload your
README.mdand include your GitHub repository link in the Submission Notes box below.
- Option A (Recommended): Upload a single
- 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 (
.envgit-ignored; secrets via environment only).
- Run your full test suite (