promptdojo_

Docker runtime and config shape — step 1 of 7

Docker and config: making "works on my machine" a guarantee

Your serving code depends on a Python version, a pile of packages, and system libraries. Docker freezes all of it into an image — a snapshot that runs identically on your laptop, CI, and the cloud. "Works on my machine" becomes "ships my machine."

One honest caveat before the tour: the image is frozen, but the build is only as reproducible as your pins. python:3.12-slim is a moving tag and an unpinned requirements.txt floats — so two builds a month apart can differ. Pin dependency versions (and, for strict builds, the base image digest) or the guarantee quietly weakens.

The Dockerfile, annotated

FROM python:3.12-slim                 # exact base, not "whatever's installed"
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt   # deps cached as a layer
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Each line is a cached layer — order matters: dependencies change rarely and copy first, code changes often and copies last, so rebuilds after a code edit reuse the expensive pip layer. Build and run: docker build -t churn-api . then docker run -p 8000:8000 churn-api. This image is also chapter 43's reproducibility story applied to serving: the runtime is now versioned, like the data and the weights. (One ML-specific note: model weights usually don't go in the image — the container fetches its model from the registry at startup, so deploying a new model doesn't mean rebuilding the runtime.)

Config: the 12-factor rule

The same image must run in staging and production — so anything that differs between environments cannot be baked into the image. It arrives from outside, as environment variables. Run the editor: one load_config(), two environments, zero code changes. This is chapter 18's env-var practice promoted to architecture — and secrets follow chapter 18 exactly: injected at runtime by the platform's secret manager, never COPY'd into an image (images get pushed to registries; a secret in a layer is a secret published).

Where AI specifically gets this wrong

  • Hardcoded config. Generated servers embed ports, model paths, and stage names in code — every environment now needs its own build, which defeats the entire point.
  • .env files and keys COPY'd into images. Chapter 18's leak, containerized and pushed to a registry.
  • No .dockerignore. It's the gitignore of images — maintain it, or the leak above is one careless COPY . . away.
  • Layer order that busts the cache. COPY . . before the pip install re-installs everything on every code change. Ten-minute rebuilds forever, for a two-line ordering mistake.