Docker image optimization cheatsheet.
Why size matters
- Faster push/pull (CI, deploys).
- Smaller attack surface.
- Cheaper registry storage.
- Faster cold starts in serverless.
Choose base image
FROM python:3.13 # ~1GB (full debian + dev tools)
FROM python:3.13-slim # ~150MB (debian-slim, runtime only)
FROM python:3.13-alpine # ~50MB (musl libc — beware C extensions)
FROM gcr.io/distroless/python3:debug # ~40MB
FROM scratch # empty (static binaries only)
For Python with C extensions: slim is safer than alpine. For Go: scratch or distroless.
Multi-stage
FROM python:3.13 AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y build-essential
COPY requirements.txt .
RUN pip install --user -r requirements.txt
FROM python:3.13-slim
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
CMD ["python", "app.py"]
Builder has compilers; final image only runtime deps.
Layer order: stable → volatile
# Bad: code change invalidates all layers below
FROM node:20
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
# Good
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
Combine RUN
# BAD: 2 layers, apt cache in image
RUN apt-get update
RUN apt-get install -y curl
# GOOD: 1 layer, clean cache
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
–no-install-recommends
RUN apt-get install -y --no-install-recommends curl ca-certificates
Skips suggested packages.
–no-cache-dir for pip
RUN pip install --no-cache-dir -r requirements.txt
BuildKit cache mount
# syntax=docker/dockerfile:1.7
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
Persists pip cache between builds — but doesn’t bake it into image. Best of both worlds.
Squash with multi-stage
Many layers are fine if needed; final image is unioned. The trick: only keep what you need in final stage.
.dockerignore
.git
.venv
node_modules
__pycache__
*.pyc
.env
*.log
dist
build
.next
coverage
.DS_Store
Reduces build context.
Check image size
docker images myapp
docker history myapp
docker history --no-trunc myapp | head -20
dive myapp # https://github.com/wagoodman/dive
dive shows layer-by-layer wasted bytes.
strip / compress
For static binaries:
RUN strip /app/binary
For node:
npm prune --production
Pruning Python pycache
RUN find /usr/lib/python3 -depth -type d -name __pycache__ -exec rm -rf {} +
Or set PYTHONDONTWRITEBYTECODE=1.
Slim Node.js
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
CMD ["node", "server.js"]
For Next.js standalone output:
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
CMD ["node", "server.js"]
Go static
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /out/app .
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]
Image ~10MB.
Rust static
FROM rust:1.78 AS build
WORKDIR /src
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl
FROM scratch
COPY --from=build /src/target/x86_64-unknown-linux-musl/release/app /app
ENTRYPOINT ["/app"]
Distroless
FROM gcr.io/distroless/python3-debian12
WORKDIR /app
COPY --from=build /app /app
USER nonroot:nonroot
CMD ["app.py"]
No shell, no package manager, no debug tools. Best for production.
:debug variant has a shell for troubleshooting.
Avoid common bloat
- Test files / docs / examples in production image.
npm installinstead ofnpm ci --omit=dev..gitdirectory copied in.- Logs / cached data baked in.
- Multiple Python versions.
Image layers tip
docker image inspect myapp --format='{{json .RootFS.Layers}}' | jq length
Fewer is better but not a strict rule. Caching benefits > raw layer count.
Common mistakes
apt-get installthenapt-get cleanin separate RUNs → cache leaked.RUN cd /dir && cmd— cd doesn’t persist; use WORKDIR.- COPY a giant context.
FROM ubuntuinstead of:slim.- Not pruning dev dependencies before final stage.
Read this next
If you want my distroless templates (Python/Go/Node), they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .