Home/Engineer/Docker / Containers/Docker Multi-stage Builds — …
Docker / ContainersIntermediateUbuntuDebianCentOSdockerdockerfile

Docker Multi-stage Builds — Minimizing Image Size

This guide shows how to cut image size dramatically by stripping build dependencies with multi-stage builds, using Node.js, Go, and Java examples.

Contents
  1. What Is a Multi-stage Build?
  2. Node.js Example
  3. Go Example (Static Binary)
  4. Java / Spring Boot Example
  5. Building a Specific Stage
  6. BuildKit Cache Optimization
  7. Image Size Comparison Tips

What Is a Multi-stage Build?

A technique that uses multiple FROM statements in a single Dockerfile so build tools stay in the first stage, and only the runtime artifacts are copied into the final image.

Regular build: build tools + source code + artifacts → large image Multi-stage: copy artifacts only → minimized image size


Node.js Example

Dockerfile
# ── Stage 1: Install dependencies + build ──────────────────
FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# ── Stage 2: Runtime image ──────────────────────────
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

# Copy build artifacts only
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

# Non-root user
RUN addgroup -S app && adduser -S app -G app
USER app

EXPOSE 3000
CMD ["node", "dist/server.js"]

Go Example (Static Binary)

Dockerfile
# ── Stage 1: Build ────────────────────────────────
FROM golang:1.22-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server

# ── Stage 2: scratch (minimal image) ───────────────
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

EXPOSE 8080
ENTRYPOINT ["/server"]

scratch image: binary only, no OS — typically a few MB


Java / Spring Boot Example

Dockerfile
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar --no-daemon

FROM eclipse-temurin:21-jre AS runner
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Building a Specific Stage

Bash
# Build only up to the builder stage (for debugging)
docker build --target builder -t myapp:builder .

# Final image
docker build -t myapp:latest .

BuildKit Cache Optimization

Dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app

# Copy package files first → reuse cache when source changes
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci

COPY . .
RUN npm run build
Bash
# Build with BuildKit enabled
DOCKER_BUILDKIT=1 docker build -t myapp .

Image Size Comparison Tips

Bash
# Check image size
docker images myapp

# Analyze size by layer
docker history myapp:latest

# Analyze layers with the dive tool
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive myapp:latest
#docker#dockerfile
Editorial note

AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us

Topic-related official docs (auto-linked · not per-claim sources)Docker documentation ↗Node.js documentation ↗Oracle Java documentation ↗

Questions & answers

Ask a question about this guide. We'll review and reply.