← Back to Blog

Docker Multi-Stage Builds: Smaller Images, Faster Deploys

May 28, 2026 3 min read By CodeTidy Team

The Docker Image Size Problem

Have you ever wondered why your Docker images are so large, even when you're only deploying a small application? You're not alone. Many developers struggle with bloated images, which can slow down deployment times and increase storage costs.

Table of Contents

  • Understanding Docker Multi-Stage Builds
  • The FROM ... AS builder Pattern
  • Copying Artifacts and Caching
  • Optimizing Image Size with Distroless Bases
  • Real-World Examples: Go, Rust, and Node
  • Key Takeaways
  • FAQ

Understanding Docker Multi-Stage Builds

Docker multi-stage builds are a game-changer for optimizing image size. The idea is simple: instead of building a single, monolithic image, we break the build process into multiple stages. Each stage is a separate image, and we can selectively copy artifacts from one stage to another. This approach allows us to keep only the necessary files in the final image, reducing its size.

The FROM ... AS builder Pattern

The FROM ... AS builder pattern is the foundation of multi-stage builds. We use this syntax to define a new stage, which we can then reference later in the Dockerfile. For example:

# Stage 1: builder
FROM golang:alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Stage 2: final
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/main .
CMD ["./main"]

In this example, we define a builder stage based on the golang:alpine image. We copy the go.mod and go.sum files, download the dependencies, and build the Go application. Then, in the final stage, we copy the compiled binary from the builder stage and set it as the command to run.

Copying Artifacts and Caching

When copying artifacts from one stage to another, we use the COPY --from=<stage_name> syntax. This allows us to selectively copy files and directories, reducing the final image size. We can also use the --cache-from flag to enable caching, which can significantly speed up the build process.

Optimizing Image Size with Distroless Bases

Distroless bases are a type of base image that contains only the minimal dependencies required to run an application. By using a distroless base, we can reduce the image size even further. For example:

# Stage 1: builder
FROM golang:alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Stage 2: final
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=builder /app/main .
CMD ["./main"]

In this example, we use the gcr.io/distroless/static:latest image as the base for the final stage. This image contains only the minimal dependencies required to run a statically linked binary, resulting in a much smaller image size.

Real-World Examples: Go, Rust, and Node

Here are some real-world examples of using multi-stage builds with different programming languages:

  • Go:
# Stage 1: builder
FROM golang:alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Stage 2: final
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=builder /app/main .
CMD ["./main"]
  • Rust:
# Stage 1: builder
FROM rust:alpine AS builder
WORKDIR /app
COPY Cargo.toml ./
RUN cargo build --release
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

# Stage 2: final
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/myapp .
CMD ["./myapp"]
  • Node:
# Stage 1: builder
FROM node:alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2: final
FROM gcr.io/distroless/nodejs:latest
WORKDIR /app
COPY --from=builder /app/dist .
CMD ["node", "index.js"]

Key Takeaways

  • Use multi-stage builds to reduce image size and improve deployment times.
  • Use the FROM ... AS builder pattern to define separate build stages.
  • Copy artifacts selectively using COPY --from=<stage_name>.
  • Use distroless bases to minimize image size.
  • Cache dependencies using --cache-from to speed up the build process.

FAQ

Q: What is the main benefit of using multi-stage builds?

A: The main benefit is reduced image size, which leads to faster deployment times and lower storage costs.

Q: Can I use multi-stage builds with any programming language?

A: Yes, multi-stage builds can be used with any programming language, as long as you can define separate build stages and copy artifacts selectively.

Q: How do I enable caching in multi-stage builds?

A: You can enable caching by using the --cache-from flag when building the image.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp