100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
YAML

Image Size Optimization

Techniques for shrinking Docker images through multi-stage builds, minimal base images, and layer-aware Dockerfile authoring to speed up builds and deployments.

Docker Best PracticesIntermediate9 min readJul 8, 2026
Analogies

Why Image Size Matters

Large Docker images slow down builds, CI/CD pipelines, and deployments because every layer must be pulled and pushed across the network. Smaller images also reduce the attack surface by shipping fewer binaries and libraries, and they lower storage costs for registries. Optimizing image size is one of the highest-leverage habits a container author can build.

🏏

Cricket analogy: A touring squad carrying excess baggage through every airport slows down travel and increases the risk of something going missing or being flagged at customs; keeping the kit bag lean is one of the highest-leverage habits a professional player builds, just like image size optimization.

Choosing a Minimal Base Image

The base image you choose has the single biggest impact on final image size. A full 'ubuntu' or 'debian' image can be several hundred megabytes, while 'alpine' variants are often under 10MB, and 'distroless' images ship only the application and its runtime dependencies with no shell or package manager at all.

🏏

Cricket analogy: Choosing a full first-class touring party of thirty support staff has a far bigger impact on travel costs than any other decision, while a lean core squad of essential players like a T20 franchise roster keeps logistics minimal, mirroring the base image's outsized impact on final size.

dockerfile
# Heavy base (avoid for production)
FROM python:3.12

# Much smaller alternative
FROM python:3.12-slim

# Smallest, no package manager, no shell
FROM gcr.io/distroless/python3-debian12

Multi-Stage Builds

Multi-stage builds let you compile or install dependencies in a 'builder' stage that includes compilers and dev tools, then copy only the final artifacts into a clean, minimal runtime stage. The builder stage's layers are discarded entirely from the final image.

🏏

Cricket analogy: A team's extended preseason training camp with extra coaches and equipment (builder stage) is where the real conditioning work happens, but only the final fit, match-ready squad (runtime stage) actually travels to the tournament, leaving the training camp's extra baggage behind entirely.

dockerfile
# Stage 1: build
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app

# Stage 2: minimal runtime
FROM gcr.io/distroless/static-debian12
COPY --from=builder /out/app /app
ENTRYPOINT ["/app"]

Minimizing Layers and Cleaning Up in the Same RUN

Each RUN, COPY, and ADD instruction creates a new layer. Package manager caches and temporary files added in one RUN layer are NOT removed by a cleanup command in a later layer, because earlier layers are already committed. Combine install and cleanup into a single RUN statement.

🏏

Cricket analogy: If a groundskeeper piles extra soil onto the pitch on Monday and only rakes it flat on Wednesday, the pile was already 'committed' into Monday's ground state; the fix is piling and leveling in the same session, just as combining install and cleanup into a single RUN avoids stale layers.

dockerfile
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

Running 'apt-get install' in one RUN layer and 'rm -rf /var/lib/apt/lists/*' in a separate later RUN layer does NOT shrink the image — the apt cache is still preserved in the earlier layer's diff. Always chain install and cleanup in the same RUN instruction.

Using .dockerignore and Ordering Layers for Cache Efficiency

A '.dockerignore' file prevents build context bloat (node_modules, .git, local build artifacts) from being sent to the Docker daemon, which speeds up builds. Ordering instructions from least-to-most frequently changing (dependency manifests before source code) maximizes layer cache reuse across builds.

🏏

Cricket analogy: A touring party leaves personal luggage like street clothes and gadgets out of the official team kit bag sent ahead, keeping it light, and packs in order of what's needed least first (formal wear) to what's needed most (match gear), mirroring '.dockerignore' and layer ordering.

text
.git
node_modules
*.log
dist/
.env

Tools like 'docker image ls', 'docker history <image>', and 'dive' let you inspect per-layer size contributions so you can find and eliminate the largest offenders in an existing Dockerfile.

  • Prefer slim, alpine, or distroless base images over full OS images
  • Multi-stage builds discard build-time tools from the final image
  • Combine package install and cache cleanup into a single RUN instruction
  • Use .dockerignore to keep the build context small
  • Order Dockerfile instructions to maximize layer cache hits
  • Inspect image layers with 'docker history' or 'dive' to find bloat

Practice what you learned

Was this page helpful?

Topics covered

#YAML#DockerKubernetesStudyNotes#DevOps#ImageSizeOptimization#Image#Size#Optimization#Matters#StudyNotes#SkillVeris