Dockerizing Python
Production Python containers use slim base images, multi-stage builds, non-root users, and pinned dependencies via uv or pip - producing small, reproducible images for ECS, Kubernetes, or Fly.
Recipe
FROM python:3.14-slim AS builder
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen --no-dev
FROM python:3.14-slim
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY app ./app
ENV PATH="/app/.venv/bin:$PATH"
USER 1000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]When to reach for this:
- Deploy FastAPI/Django/Flask to any container platform
- Reproducible builds from lockfiles in CI
- Isolate dependencies from host Python
- Horizontal scaling with identical images per replica
Working Example
Multi-stage Dockerfile with uv, non-root user, and health-friendly defaults.
# syntax=docker/dockerfile:1
FROM python:3.14-slim-bookworm AS builder
WORKDIR /build
RUN pip install --no-cache-dir uv==0.6.0
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev
FROM python:3.14-slim-bookworm AS runtime
WORKDIR /app
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/app/.venv/bin:$PATH"
RUN useradd --create-home --uid 1000 appuser
COPY --from=builder /build/.venv /app/.venv
COPY app ./app
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
CMD ["gunicorn", "-w", "2", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "app.main:app"]docker build -t myapp:local .
docker run --rm -p 8000:8000 -e LOG_LEVEL=info myapp:localWhat this demonstrates:
- Builder stage installs deps; runtime copies only
.venv PYTHONUNBUFFEREDensures logs flush promptly- Non-root
appuserreduces container breakout impact HEALTHCHECKcomplements orchestrator probes
Deep Dive
Image Size Tactics
| Technique | Savings |
|---|---|
slim base | Smaller OS layer |
| Multi-stage | No build tools in final image |
.dockerignore | Skip .venv, tests, .git |
| No pip cache | --no-cache-dir |
.dockerignore Essentials
.git
.venv
__pycache__
.pytest_cache
tests/
Python Notes
# Pin digest for supply-chain safety
FROM python:3.14-slim-bookworm@sha256:... AS runtimeGotchas
- Copying entire repo before dependency install - cache bust every commit. Fix: copy lockfiles first,
RUN uv sync, then copy app code. - Running as root - container escape impact. Fix:
USERnon-root with writable only needed dirs. - Dev dependencies in prod image - bloat and attack surface. Fix:
--no-devsync. - Binding 127.0.0.1 in CMD - unreachable from outside container. Fix:
0.0.0.0. - Secrets in ENV or layers - visible in image history. Fix: inject at runtime from secret store.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| AWS Lambda container | Spiky low-ops | Long-lived connections |
| PaaS buildpacks | Fastest MVP | Need full Dockerfile control |
| Nix/conda images | Scientific stack pinning | Simple web API |
FAQs
uv vs pip in Docker?
uv faster and lockfile-native - recommended for Python 3.14 projects per manifest pins.
Do I need gunicorn in container?
Uvicorn alone works for small services; Gunicorn manages multiple workers in one container.
How do I run migrations?
Separate init container or job - not in the same CMD as web server unless carefully sequenced.
Where do static files go?
Collect at build (django collectstatic) into image or upload to S3/CDN at deploy.
How do I debug locally?
docker compose with volume mount for dev only - prod image should not mount source over image.
ARM vs AMD images?
Build multi-arch with buildx when deploying to Graviton or Apple Silicon dev matching prod.
How do I scan images?
CI runs Trivy/Grype on push; patch base image regularly.
What about distroless?
Possible with venv copied in - harder debugging; slim Debian is pragmatic default.
How do I pass config?
Runtime env vars and secrets - not baked into image layers.
How big should images be?
Track size in CI; investigate when web API image exceeds few hundred MB without ML deps.
Related
- Deployment Basics - WSGI/ASGI models
- Gunicorn & Uvicorn - worker CMD
- Configuration & Secrets in Prod - runtime env
- CI/CD Pipelines - build and push images
- Deployment Best Practices - container checklist
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.