Skip to content
intermediate Phase 12 · Docker & Applications

Dockerizing Python Apps

Create Dockerfiles for Python apps with virtual environments.

1h
0 problems
Topic Progress 0%

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?

Question 1 options

2. What does PYTHONUNBUFFERED=1 fix?

Question 2 options

3. Why python -m app instead of CMD ["app.py"]?

Question 3 options

Flashcards

Question

venv in image

Answer

Create /opt/venv in the builder, COPY it forward, put it on PATH — deps without toolchain.

Question

PYTHONUNBUFFERED

Answer

Disables output buffering so docker logs streams in real time.

Question

PYTHONDONTWRITEBYTECODE

Answer

Skips .pyc files — smaller images and no stale-cache surprises.

Question

Native build deps

Answer

gcc/libpq-dev belong in the builder stage; runtime needs only the compiled result.

Question

python -m pkg

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.

Continue Learning