Skip to main content

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 .dockerignore to 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

Example: Basic .dockerignore

node_modules
*.log
.git
.DS_Store
.env
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
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"]
Clean Package Installation Remove dev dependencies after build RUN npm ci --only=production
Use .dockerignore Exclude unnecessary files from build context
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output

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"

Example: Analyze image size breakdown

docker inspect myapp:latest | jq '.[0].RootFS.Layers'
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+

Example: Squashing image layers

docker build --squash -t myapp:latest .
Note: Squashing is not supported in all Docker versions and may not work as expected in some environments.

Was this article helpful?