Back to Home

Python Docker Optimization: -43-57% size

Article breaks down optimization of Python Docker images: multi-stage build reduces size by 43%, scratch — by 57%. Dockerfile examples, benchmarks, analysis with dive. Suitable for middle/senior developers.

Python Docker: size -57% from scratch with multi-stage
Advertisement 728x90

Optimizing Python Docker Images: Reducing Size by 43–57%

Multi-stage builds and Python slim images can shrink the runtime image size from 895 MB to 508 MB (-43%). Build time after code changes drops from 56 to 16 seconds. This speeds up Kubernetes deployments, saves storage, and reduces traffic.

Optimization addresses key issues: rapid pod scaling during traffic spikes and reduced user latency. Dependencies are managed with Poetry, and tests run in the builder stage.

.dockerignore and Slim Images

Exclude tests and unnecessary files via .dockerignore. Tests are only needed during the build stage.

Google AdInline article slot

Switch to python:3.13-slim-bookworm. The full image adds hundreds of MB.

Disable pip/Poetry cache with flags:

  • pip: --no-cache-dir
  • Poetry: poetry cache clear --all pypi

Multi-Stage Build: Dockerfile Breakdown

The builder stage installs Poetry, dependencies, and runs pytest. The runtime stage copies only site-packages, binaries (gunicorn, uvicorn), and code.

Google AdInline article slot
# Builder stage
FROM python:3.13-slim-bookworm AS builder
ENV PYTHONUNBUFFERED=1 \
    LANG=C.UTF-8 \
    PYTHONDONTWRITEBYTECODE=1

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends \
      curl \
      gcc \
      libc-dev \
    && pip install --no-cache-dir 'poetry==2.3.2' \
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

WORKDIR /code

COPY pyproject.toml poetry.lock* ./

RUN poetry config virtualenvs.create false \
  && poetry install --no-root --no-dev --no-interaction --no-ansi \
  && poetry cache clear --all pypi --no-interaction || true \
  && rm -rf ~/.cache/pip

COPY . .
RUN pytest

# Runtime stage
FROM python:3.13-slim-bookworm
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    LANG=C.UTF-8

WORKDIR /code

COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin/gunicorn /usr/local/bin/gunicorn
COPY --from=builder /usr/local/bin/uvicorn /usr/local/bin/uvicorn

COPY --from=builder /code /code

Basic Optimization Results:

| Metric | Before | After | Change |

|---------|------|-------|-----------|

Google AdInline article slot

| Image Size, MB | 894.84 | 508 | -43% |

| Full Build Time, s | 58 | 315 | +443% |

| Build Time After Edits, s | 56 | 16 | -71% |

The first build is slower due to builder layers, but caching speeds up iterations.

Advanced Methods: Chainguard and Manual Analysis

Chainguard Images

Chainguard excludes shell, apt/apk, minimizing attack surface. No CVEs in base images. Savings: 50–150 MB.

Drawbacks:

  • Debugging without shell
  • Free only for latest tag
  • Paid versions are expensive

Suitable for high-security projects.

Manual Analysis with dive

Analyze layers:

docker history --format "{{.CreatedBy}}: {{.Size}}" image:tag
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive:latest image:tag

Focus on large runtime layers. Collapse directories (Shift+Space), remove unnecessary libs. Requires Linux expertise, rarely pays off.

Extreme Optimization: scratch

Runtime on scratch—copy only required libc. Size drops to 384 MB (-57% from original, -24% from multi-stage).

Drawbacks:

  • Debugging impossible
  • New deps require manual lib addition
  • Debugging time: 1+ hour

Relevant for thousands of pods, Serverless (Knative, Fargate). Harder in Python than Go.

Size Comparison:

  • Without multi-stage: 894 → 384 MB (-57%)
  • Multi-stage: 508 → 384 MB (-24%)

Optimizing Build Time

Copy pyproject.toml/poetry.lock BEFORE code:

  • COPY pyproject.toml poetry.lock* ./
  • RUN poetry install
  • COPY . .
  • RUN pytest

Dependency cache persists with code changes. Speedup: 58 → 14 s (x4).

Key Takeaways

  • Multi-stage + slim: -43% size, -71% iteration time.
  • .dockerignore + no-cache: excludes tests and cache from runtime.
  • COPY order: dependencies cached separately from code.
  • Scratch: -57%, but for extreme cases (thousands of pods).
  • Chainguard/dive: for security and fine-tuning.

— Editorial Team

Advertisement 728x90

Read Next