Stage 4 · Provision
Building Images
Image Size Optimization
Measure image size with docker image ls, remove package caches, and inspect layers with dive.
Why Size Matters
Image size directly affects pull time, startup speed, storage costs, and attack surface. A 2GB image takes minutes to pull across a network and stores unnecessary files. A 20MB image pulls in seconds, starts instantly, and contains only what is needed. Smaller images are faster, cheaper, and more secure.
Measuring Size
# List all images with sizes
docker image ls
# Compact format
docker image ls --format "table {{.Repository}} {{.Tag}} {{.Size}}"
# Show image size in human-readable format
docker image inspect myapp --format '{{.Size}}'
# Show the virtual size (all layers including base)
docker image inspect myapp --format '{{.VirtualSize}}'docker image ls shows the compressed size on disk. The virtual size includes all layers from the base image. Two images sharing layers will show the same layer size only once in total disk usage.
Reducing Base Image Size
The base image is often the largest contributor to total image size. Switching from ubuntu to alpine or distroless can reduce your image by hundreds of megabytes. Start every optimization by evaluating whether you need a full distribution.
| Base Image | Typical Size | Tradeoff |
|---|---|---|
| ubuntu:22.04 | 78MB | Full compatibility, large |
| debian:bookworm-slim | 75MB | Slim variant, still large |
| alpine:3.19 | 7MB | Small, musl libc |
| distroless/static | 2MB | No libc, static binaries only |
| scratch | 0MB | Nothing included |
Removing Caches
Package managers leave caches, logs, and metadata after installation. These files are useful during the build but waste space in the final image. Clean them in the same RUN instruction that installs packages to avoid keeping them in a previous layer.
# Bad — cache persists in the RUN layer
RUN apt-get update
RUN apt-get install -y curl
# apt cache is still in the image
# Good — clean in the same layer
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# Good — use --no-cache for apk
RUN apk add --no-cache curlEach RUN instruction creates a layer. If you clean the cache in a separate RUN, the previous layer still contains the cache. Combine install and cleanup in one RUN to avoid this.
apt-get install --no-install-recommends skips recommended packages that are not strictly required. This can reduce image size significantly. For example, installing curl without recommends saves about 30MB of additional packages.
Using dive
dive is a tool that lets you explore every layer of a Docker image, showing which files were added, modified, or deleted in each layer. It reveals hidden bloat — files you did not realize were in the image.
# Install dive
brew install dive # macOS
# or
docker pull wagoodman/dive
# Analyze an image
dive myapp:latest
# Analyze during build
CI=true dive myapp:latest
# In the TUI:
# - Arrow keys navigate layers
# - Tab toggles file details
# - Space marks files for analysisdive shows a tree view of the filesystem at each layer. Look for large files, unnecessary caches, or files that should be in .dockerignore. It also gives an efficiency score for your image.
.dockerignore Optimization
Files excluded by .dockerignore are never sent to the build context, but files included in the context may still end up in the image if your COPY instructions are too broad. A well-crafted .dockerignore prevents accidental inclusion of unnecessary files.
# Version control
.git
.gitignore
# Dependencies
node_modules
vendor
__pycache__
# Build output
dist
build
target
# Development
.env
.env.*
*.log
coverage
.nyc_output
# IDE
.vscode
.idea
*.swp
# Docker files
Dockerfile*
docker-compose*.yml
# Documentation
*.md
LICENSEThis .dockerignore excludes common large directories and sensitive files. Copy this as a starting point and customize for your project. A good .dockerignore can reduce build context from hundreds of MB to a few KB.
Docker sends the entire build context to the daemon before building. If your directory contains a 2GB node_modules folder, Docker sends all 2GB even if you never COPY it. The .dockerignore prevents this transfer.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.