Module 7 · DevOps · Drills
These are the commands and files you'll run a hundred times on the job. Type each one yourself in a real terminal before revealing the solution — fluency here is what makes you look like a professional.
Drill 1 git flow
From scratch: initialise a repo, commit everything, create a feature/login branch, then push main to a remote called origin.
git init git add . git commit -m "Initial commit" git checkout -b feature/login # new branch + switch git checkout main # back to main git remote add origin git@github.com:you/app.git git push -u origin main
The -u sets origin/main as the upstream, so later you can just type git push.
Drill 2 .gitignore
Write a .gitignore for a Python project that excludes byte-code, the virtual environment, the secrets file, and editor noise.
.gitignore
__pycache__/ *.pyc .venv/ venv/ .env .vscode/ .DS_Store
If .env was already committed, add it here and run git rm --cached .env to stop tracking it.
Drill 3 dockerfile
Write a Dockerfile for the FastAPI backend: Python 3.13 slim, install from requirements.txt, run on port 8000 reachable from outside the container.
Dockerfile
FROM python:3.13-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["fastapi", "run", "main.py", "--port", "8000", "--host", "0.0.0.0"]
Build and run: docker build -t app . then docker run -p 8000:8000 app. Drop --host 0.0.0.0 and you won't reach it.
Drill 4 compose
Write a compose.yaml running two services: your api (built locally, port 8000) and a db using Postgres 17, with a volume so data survives restarts.
compose.yaml
services:
api:
build: .
ports:
- "8000:8000"
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Start it with docker compose up -d. The api reaches Postgres at host db — the service name is the hostname.
Drill 5 secrets
This code has a hardcoded secret. Move it into an environment variable read by pydantic-settings.
# Before — DON'T do this OPENAI_API_KEY = "sk-live-abc123"
.env (git-ignored)
OPENAI_API_KEY=sk-live-abc123
config.py
from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): openai_api_key: str model_config = SettingsConfigDict(env_file=".env") settings = Settings() # use settings.openai_api_key — never the literal
Add .env to .gitignore first. In production you set OPENAI_API_KEY on the host, no file at all.
Dockerfile for the FastAPI backend, a compose.yaml wiring it to Postgres, and a .env holding the config — kept out of Git. This is the exact deliverable that makes next lesson's deploy trivial.
Build · containerise DocChat
Write all three files, then bring the stack up and confirm the API is reachable.
Dockerfile
FROM python:3.13-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["fastapi", "run", "main.py", "--port", "8000", "--host", "0.0.0.0"]
compose.yaml
services:
api:
build: .
ports:
- "8000:8000"
environment:
DATABASE_URL: ${DATABASE_URL}
OPENAI_API_KEY: ${OPENAI_API_KEY}
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_USER: docchat
POSTGRES_PASSWORD: secret
POSTGRES_DB: docchat
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
.env (git-ignored)
DATABASE_URL=postgresql://docchat:secret@db:5432/docchat OPENAI_API_KEY=sk-...
# bring it up; Compose auto-loads .env for ${...} substitution docker compose up -d curl http://localhost:8000/docs # FastAPI is alive
The ${DATABASE_URL} syntax pulls values from .env at startup — so secrets live in one ignored file, not in compose.yaml. Confirm git status never lists .env.
Click a card to flip it. Say the answer out loud before you flip — that's the rep that builds storage strength.
.gitignore for?.venv/, caches — keeping them out of history.git checkout -b x do?x and switches to it in one step.CMD for FastAPI?CMD ["fastapi","run","main.py","--port","8000","--host","0.0.0.0"]docker compose do?compose.yaml with one command..env locally, host config in production. Never in code.Tick each only if you can do it without looking:
.gitignore that excludes .env and .venv/compose.yaml running the app plus Postgrespydantic-settings