Challenge 1: Convert a Single-Stage Dockerfile — Possible Solution ==================================================================== FROM python:3.12 AS builder WORKDIR /app RUN apt-get update && apt-get install -y build-essential COPY . . RUN pip install cython && python setup.py build_ext --inplace RUN pip install --target=/app/deps -r requirements.txt FROM python:3.12-slim WORKDIR /app COPY --from=builder /app/deps /usr/local/lib/python3.12/site-packages COPY --from=builder /app/*.so ./ COPY --from=builder /app/app.py . CMD ["python", "app.py"] WHY THIS WORKS AS AN ANSWER ------------------------------ The builder stage keeps everything the original single-stage Dockerfile needed to actually BUILD the extension: build-essential (the compiler toolchain), Cython, and the full source tree — none of which the running application needs once the compiled .so extension file already exists. The final stage starts fresh FROM python:3.12-slim — a smaller base image than the full python:3.12 used for building — and selectively copies only three things via COPY --from=builder: the installed runtime dependencies, the compiled .so extension, and the actual application script. The build-essential toolchain, Cython itself, and the raw source files used only for compiling never make it into this final image at all, exactly the size and attack-surface reduction this chapter's example demonstrated with Node.js.