## Multi-Stage Dockerfile Multi-stage builds keep your final image small by separating build-time dependencies from runtime. ```dockerfile FROM python:3.12-slim AS builder WORKDIR /app RUN apt-get update && apt-get install -y gcc libpq-dev COPY requirements.txt . RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt FROM python:3.12-slim WORKDIR /app RUN apt-get update && apt-get install -y libpq5 && rm -rf /var/lib/apt/lists/* COPY --from=builder /wheels /wheels RUN pip install --no-cache /wheels/* COPY . . RUN useradd -r appuser && chown -R appuser /app USER appuser EXPOSE 8000 CMD ["gunicorn", "myapp.wsgi", "--bind", "0.0.0.0:8000"] ``` ## Key Principles - **Non-root user** — never run production containers as root - **Layer caching** — copy `requirements.txt` before source code so pip only reinstalls on dependency changes - **No secrets in image** — pass environment variables at runtime, never bake them in - **.dockerignore** — exclude `venv/`, `*.pyc`, `.env`, `media/` ## Build & Run ```bash docker build -t myapp:latest . docker run -p 8000:8000 --env-file .env myapp:latest ```