Module 7 · DevOps · Drills
Reading about deployment is not deploying. Write each config yourself, run it against a real free-tier host, and only then reveal the solution. The win here is a clickable URL.
Drill 1 github actions
Write a minimal .github/workflows/ci.yml that, on every push, checks out the code, sets up Python 3.12, installs requirements.txt, and runs pytest.
.github/workflows/ci.yml
name: CI on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - run: pip install -r requirements.txt - run: pytest -q
The file lives at that exact path — GitHub discovers any .yml under .github/workflows/ automatically.
Drill 2 env vars
List the environment variables DocChat needs in production, split by where each one lives (frontend host vs backend host).
# Vercel (frontend) NEXT_PUBLIC_API_URL # the public backend URL the browser calls # Render / Railway / Fly.io (backend) DATABASE_URL # the Neon Postgres connection string OPENAI_API_KEY # RAG embeddings + LLM calls JWT_SECRET # signs/verifies auth tokens FRONTEND_ORIGIN # the Vercel domain, used for CORS
Only NEXT_PUBLIC_API_URL is browser-exposed. The rest are server-side secrets and must never carry the NEXT_PUBLIC_ prefix or land in git.
Drill 3 cors
The frontend is live at https://docchat.vercel.app. Write the FastAPI CORS middleware so the backend accepts requests from that origin.
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://docchat.vercel.app"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
Use the exact scheme + host, no trailing slash. Don't ship allow_origins=["*"] with credentials — browsers reject that combination.
Drill 4 vercel deploy
Outline the steps to deploy the Next.js frontend to Vercel from a GitHub repo, including where env vars go.
# 1. Push the repo to GitHub. # 2. vercel.com -> Import Project -> pick the repo. # Vercel auto-detects Next.js (no build config needed). # 3. Settings -> Environment Variables: # NEXT_PUBLIC_API_URL = https://docchat-api.onrender.com # 4. Deploy. Every later `git push` to main auto-deploys; # every pull request gets its own preview URL.
No deploy command by hand — the GitHub connection makes push the trigger.
Drill 5 backend deploy
Outline how you deploy the FastAPI backend as a Docker container (e.g. on Render), reusing the 7.1 Dockerfile.
# 1. Ensure the repo has the Dockerfile from Lesson 7.1. # CMD: uvicorn app.main:app --host 0.0.0.0 --port $PORT # 2. Render -> New -> Web Service -> connect the repo. # Render detects the Dockerfile and builds the image. # 3. Set env vars in the dashboard: # DATABASE_URL, OPENAI_API_KEY, JWT_SECRET, FRONTEND_ORIGIN # 4. Deploy -> Render gives an HTTPS URL (TLS automatic). # 5. Run migrations once: alembic upgrade head against DATABASE_URL.
Bind to 0.0.0.0 and the host's $PORT — not 127.0.0.1:8000, which only works locally.
Build · deploy checklist + CI
Write the ordered checklist and the ci.yml together.
# DocChat go-live checklist # [ ] .env is gitignored; no secrets committed anywhere # [ ] Neon: create project, copy DATABASE_URL # [ ] alembic upgrade head (against the Neon DATABASE_URL) # [ ] Render: connect repo, set DATABASE_URL, OPENAI_API_KEY, # JWT_SECRET, FRONTEND_ORIGIN -> deploy -> note API URL # [ ] CORS allow_origins includes the Vercel domain -> push # [ ] Vercel: import repo, set NEXT_PUBLIC_API_URL=API URL -> deploy # [ ] Smoke test: sign up, upload a doc, ask a question end-to-end # [ ] CI green on main
.github/workflows/ci.yml
name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: pip install -r requirements.txt - name: Lint run: ruff check . - name: Run tests run: pytest -q
Order matters: database before backend, backend URL before the frontend env var, CORS before the first real browser call. The CI file makes every future push prove itself.
Click a card to flip it. Say the answer out loud before you flip — that's the rep that builds storage strength.
Neon (or Supabase). You get a DATABASE_URL.on: key — e.g. push and pull_request to the repo.ruff), then pytest. Red blocks the merge.git push to main auto-deploys, PRs get preview URLs.allow_origins the prod frontend.Tick each only if you can do it without looking:
DATABASE_URLallow_origins for the prod domainci.yml that runs pytest on push, and DocChat is live