Slim Base and a Virtualenv
Slim Base and a Virtualenv
The venv travels with the image so the app never depends on system site-packages:
FROM python:3.12-slim AS build
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
COPY --from=build /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
COPY . .
CMD ["python", "-m", "app"]
Creating the venv in a builder stage keeps compilers and pip's download cache out of the final image, while the copied /opt/venv is a self-contained, relocatable environment.
Layer Order and Runtime Hygiene
Layer Order and Runtime Hygiene
Dependencies change less often than source — encode that in the Dockerfile: COPY requirements.txt before COPY . ., and add a requirements hash if you want stricter cache keys. Runtime flags worth setting by default:
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
PYTHONUNBUFFERED streams prints straight to stdout, so docker logs shows progress live instead of losing it in a buffer. Run as non-root with useradd and USER app, and use python -m package as CMD so relative imports resolve with package context.
Pitfalls and Interview Angles
Pitfalls and Interview Angles
Native extensions. Packages like psycopg2 (source build) need gcc and libpq-dev; install them in the builder stage and copy only the compiled venv — never apt-get build tools into runtime.
Requirements drift. Pin versions (fastapi==0.115.0) or, better, a compiled lock file; an unpinned requirements.txt makes yesterday's image unreproducible.
Dev servers in production. uvicorn --reload watches files that do not exist in the final image; run without reload and let the platform restart on failure.
Interview: explain the two-stage layout, why -m beats a script path, and where system libraries fit when wheels are not available.
Quiz
1. Why create the virtualenv in a builder stage and COPY it over?
2. What does PYTHONUNBUFFERED=1 fix?
3. Why python -m app instead of CMD ["app.py"]?
Flashcards
Question
venv in image
Click to reveal answer
Answer
Create /opt/venv in the builder, COPY it forward, put it on PATH — deps without toolchain.
Question
PYTHONUNBUFFERED
Click to reveal answer
Answer
Disables output buffering so docker logs streams in real time.
Question
PYTHONDONTWRITEBYTECODE
Click to reveal answer
Answer
Skips .pyc files — smaller images and no stale-cache surprises.
Question
Native build deps
Click to reveal answer
Answer
gcc/libpq-dev belong in the builder stage; runtime needs only the compiled result.
Question
python -m pkg
Click to reveal answer
Answer
Executes a module with package context so relative imports work in containers.
Revision Notes
Key Takeaways
- 1. Copy the venv, not the toolchain
- 2. Requirements first, source second — cache-friendly layers
- 3. Unbuffered logs and non-root users by default
Interview Tips
- • Walk the two stages and say exactly what crosses the boundary
- • Explain how you would handle a package with no prebuilt wheel
Cheat Sheet
python:3.12-slim; venv in builder → COPY /opt/venv; requirements before source; PYTHONUNBUFFERED=1; non-root USER; build deps stay in builder.