Skip to course content
Free FastAPI backend course

FastAPI for AI Backend Development

Unit 14.02: A container that starts the same way every time

The container should start the same way every time, and layer ordering is what makes rebuilds fast.

Dependencies first, source last

A Dockerfile with each line's reason.

The code lists them.

DOCKERFILE = [
    ("FROM python:3.12-slim",              "pinned minor version"),
    ("WORKDIR /app",                       ""),
    ("COPY pyproject.toml ./",             "dependencies first, so the layer caches"),
    ("RUN pip install --no-cache-dir .",   "no pip cache in the image"),
    ("COPY app ./app",                     "source last; changes here skip the install"),
    ("USER 1000",                          "do not run as root"),
    ("EXPOSE 8000",                        ""),
    ('CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--workers", "4"]',
     "no --reload"),
]
for line, note in DOCKERFILE:
    print(f"{line:58} {note}")

print("""
The ordering is what makes rebuilds fast: dependencies change rarely and get
cached, source changes constantly and sits in the last layer.

`USER 1000` and the absence of `--reload` are the two lines most often missing
from a container that started life as a development one.
""")

Dependencies change rarely and sit in an early layer that caches; source changes constantly and sits last, so a code change skips the install entirely. Reversing that order means reinstalling everything on every commit.

USER 1000 and the absence of --reload are the two lines most often missing from a container that began life as a development one.

The mistake this prevents

The mistake is copying the whole directory before installing. Every source change then invalidates the dependency layer, and a one-line fix takes a full rebuild.

Takeaway

Copy dependency manifests and install before copying source, run as a non-root user, and never ship --reload.