Skip to main content

How to build and optimize Docker images for smaller, faster builds

Build lean Docker images by ordering layers for cache reuse, trimming the build context with .dockerignore, and choosing smaller base images. Measure results with docker history.

Difficulty
Beginner
Duration
30 minutes
Steps
6

Build and optimize Docker images

A Docker image bundles your application and its runtime into a portable artifact. Large images are slow to build, push, and pull, and they expand the attack surface. This tutorial shows how to keep images small and builds fast through layer ordering, a focused build context, and lean base images.

Prerequisites

  • Docker installed and running (docker --version).
  • An application directory with source code and a dependency manifest.

Steps

1. Write a baseline Dockerfile

Start with a working image, then improve it:

FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

Build it: docker build -t myapp:base .

2. Add a .dockerignore

The build context is sent to the daemon before the build starts. Exclude files you never need inside the image:

node_modules
.git
*.log
dist
.env

This speeds up COPY . . and avoids leaking secrets or local artifacts.

3. Order layers for cache reuse

Docker caches each instruction. Copy dependency manifests and install before copying the rest of the source, so code changes do not invalidate the dependency layer:

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]

Now editing server.js reuses the cached npm ci layer.

4. Choose a smaller base image

Variants like -slim or Alpine cut tens to hundreds of megabytes. Verify your app runs on the chosen libc (Alpine uses musl, which can break native modules). Pin a specific tag for reproducibility, for example node:20.11-slim.

5. Clean up in the same layer

Deleting files in a later RUN does not shrink earlier layers. Combine install and cleanup:

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

6. Measure image size

Compare before and after:

docker images myapp
docker history myapp:latest

docker history shows each layer's size so you can target the biggest contributors.

Verification

Run docker build -t myapp:opt . twice. The second build should reuse cached layers and finish quickly. Confirm the optimized image is smaller with docker images. Start a container with docker run --rm -p 3000:3000 myapp:opt and check the app responds.

Next Steps

Move to multi-stage builds to strip build tools from the final image, then explore distroless base images for the smallest, most secure runtime. Add image scanning to your pipeline to catch vulnerabilities in base layers.

Prerequisites

  • Docker installed
  • A simple application to containerize

Steps

  • 1
    Write a baseline Dockerfile
  • 2
    Add a .dockerignore
  • 3
    Order layers for cache reuse
  • 4
    Choose a smaller base image
  • 5
    Clean up in the same layer
  • 6
    Measure image size

Category

Containers