Optimizing Docker Images
1. Using Smaller Base Images
| Base Image | Size (MB) | Use Case | Notes |
|---|---|---|---|
| alpine:latest | 5.5 |
Minimal runtime environments | Small footprint, fewer vulnerabilities |
| debian:slim | 120 |
Production applications | Stable, well-supported, good for complex apps |
| node:alpine | 60 |
Node.js applications | Optimized for Node, smaller than full Debian |
| python:slim | 125 |
Python applications | Reduced size compared to full Python image |
| gcr.io/distroless/base | 15 |
Minimal runtime for compiled binaries | No shell or package manager; secure and minimal |
Example: Using Alpine as base image
FROM alpine:latest
RUN apk add --no-cache nodejs npm
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Note: Alpine uses
musl instead of glibc, which can cause compatibility issues with some binaries.2. Minimizing Layer Count
| Instruction | Description | Impact |
|---|---|---|
| COPY / ADD | Each command creates a new layer. Combine multiple files in one command to reduce layers. | Reduces image size and build time |
| RUN | Use apt-get or yum with && to chain commands in one layer. |
Reduces number of layers |
| Multi-stage builds | Use separate stages for building and running to avoid including build tools in final image. | Significantly reduces final image size |
Example: Combining RUN commands
FROM node:18
RUN apt-get update && \
apt-get install -y curl wget && \
npm install -g npm@latest
Example: Multi-stage build
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Final stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
3. Combining RUN Commands
| Command | Description | Benefit |
|---|---|---|
| RUN apt-get update && apt-get install -y package1 package2 | Combines multiple commands in a single RUN instruction | Reduces layer count and image size |
| RUN echo "Hello World" && mkdir /app | Chains shell commands with logical flow | Improves readability and reduces layers |
Example: Efficient RUN Commands
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y \
curl \
git \
vim \
&& rm -rf /var/lib/apt/lists/*
Note: Use
&& to chain commands and \ for line continuation to keep layers minimal.4. Ordering Instructions for Cache Efficiency
| Instruction | Description | Impact on Cache |
|---|---|---|
| COPY . /app | Copies all files from host to container | Invalidates cache if any file changes |
| COPY package*.json ./ | Copies package files first | Allows cache reuse for dependency installs |
| RUN npm install | Installs dependencies | Reuses cache if package files unchanged |
| COPY src/ ./src | Copies source code | Invalidates cache if src changes |
Example: Efficient Layer Ordering
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY src/ ./src
EXPOSE 3000
CMD ["node", "src/index.js"]
Note: Copying
package*.json before src/ ensures that dependency layers are cached unless package files change.5. Leveraging Build Cache Effectively
| Instruction | Description | Best Practice |
|---|---|---|
| COPY . /app | Copies entire directory | Use specific files or directories to avoid invalidating cache |
| RUN apt-get update && apt-get install -y package | Installs packages | Combine commands to reduce layers and cache hits |
| ENV NODE_ENV=production | Sets environment variable | Place near top to avoid unnecessary rebuilds |
Example: Optimizing Dockerfile for Cache
FROM node:18-alpine
WORKDIR /app
# Copy package files first to leverage cache
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port and run app
EXPOSE 3000
CMD ["npm", "start"]
Note: Docker builds use layer caching. Changes to earlier layers invalidate subsequent layers.
Cache Optimization Tips
- Order instructions from least to most frequently changing
- Copy dependency files before application code
- Avoid wildcards in COPY instructions like
COPY ./* - Use
.dockerignoreto exclude unnecessary files from context
6. Using .dockerignore to Exclude Files
The .dockerignore file works similarly to .gitignore, allowing you to specify files and directories that should be excluded from the Docker build context. This helps reduce image size, improve build times, and prevent sensitive data from being included in the image.
| Pattern | Description | Example |
|---|---|---|
| *.log | Excludes all files with the .log extension. |
*.log |
| node_modules | Excludes the node_modules directory entirely. |
node_modules |
| build/ | Excludes the build directory and its contents. |
build/ |
| !important.txt | Excludes all files except important.txt. |
!important.txt |
Note: The
.dockerignore file is processed relative to the build context root, and it's not part of the image filesystem.
7. Removing Build Dependencies
| Step | Description | Action |
|---|---|---|
| Multi-stage Builds | Use separate stages for building and running | |
| Clean Package Installation | Remove dev dependencies after build | RUN npm ci --only=production |
| Use .dockerignore | Exclude unnecessary files from build context | |
Example: Reducing Image Size with Multi-Stage Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Note: Multi-stage builds help reduce image size by only including necessary files in the final image.
Warning: Ensure all required runtime dependencies are copied to the final stage.
8. Cleaning Package Manager Cache
| Package Manager | Cleaning Command | Notes |
|---|---|---|
| apt (Debian/Ubuntu) | apt-get clean |
Removes downloaded package files from /var/cache/apt/archives/ |
| yum (CentOS/RHEL) | yum clean all |
Removes cached data from /var/cache/yum/ |
| apk (Alpine) | apk cache clean |
Removes downloaded package files from /var/cache/apk/ |
Example: Cleaning apt cache in a multi-stage build
FROM ubuntu:20.04 AS builder
RUN apt-get update && apt-get install -y some-package
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
FROM ubuntu:20.04
COPY --from=builder /app /app
Note: Always combine cache cleaning with removing package lists to maximize space savings in Docker layers.
9. Using Specific Image Tags
| Tag Type | Description | Example | Notes |
|---|---|---|---|
| latest | Points to the most recently built image for a repository | nginx:latest |
Can be misleading; image may change without notice |
| Version Tags | Explicit version numbers (e.g., 20.04, 18.04) | ubuntu:20.04 |
Recommended for production to ensure consistency |
| Digests | SHA256 hash of the image manifest | nginx@sha256:a1b2c3d4e5f6... |
Most secure; guarantees exact image content |
| Pre-release Tags | Tags like beta, rc1 |
node:18-beta |
Use with caution; may not be stable |
Example: Pulling an image with a specific digest
docker pull nginx@sha256:a1b2c3d4e5f67890123456789012345678901234567890123456789012345678
Note: Using digest tags ensures reproducible builds and avoids unexpected updates.
10. Compressing Image Layers
| Layer | Compression Method | Size Reduction | Notes |
|---|---|---|---|
| FROM | GZIP | Up to 50% | Base image layer is compressed by default |
| COPY / ADD | GZIP | Up to 70% | Files copied into image are compressed |
| RUN | None | 0% | Commands are not compressed, but can reduce layer size |
Example: Reducing Layer Size with RUN Commands
FROM alpine:latest
RUN apk add --no-cache curl
RUN curl -O https://example.com/file.tar.gz && \
tar -xzf file.tar.gz && \
rm file.tar.gz
Note: Combining multiple RUN commands into one reduces the number of layers and improves compression efficiency.
11. Analyzing Image Size
| Tool | Description | Usage |
|---|---|---|
| docker images | Lists all local Docker images with their sizes | docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | head -10 |
| docker system df | Shows disk usage of Docker objects including images | docker system df -v |
| docker inspect | Provides detailed information about an image including layer sizes | docker inspect <image-id> | grep -A 5 "RootFS" |
Note: The
docker system df command gives a high-level overview, while docker inspect provides granular details on each layer's contribution to the total image size.
12. Squashing Image Layers
Squashing Docker image layers reduces the number of layers in an image by combining multiple layers into a single layer. This can significantly reduce image size and improve performance.
| Option | Description | Notes |
|---|---|---|
| --squash | Flag to squash layers during build | Available in Docker 17.05+ |
Note: Squashing is not supported in all Docker versions and may not work as expected in some environments.