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 buildBash
# 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:latestEditorial 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.