Skip to main content

How to use multi-stage Docker builds to shrink production images

Multi-stage builds compile in one stage and copy only artifacts into a slim runtime stage, keeping compilers and dev dependencies out of production images. Use COPY --from and --target to control output.

Difficulty
Intermediate
Duration
35 minutes
Steps
6

Multi-stage Docker builds

A multi-stage build uses several FROM statements in one Dockerfile. Early stages compile or bundle the application; the final stage copies only the resulting artifacts. Build tools, source code, and dev dependencies never reach production, so the image is smaller and safer.

Prerequisites

  • Docker installed.
  • An application that has a build step (compiled binary, bundled assets, or transpiled output).

Steps

1. Identify build vs runtime needs

List what is needed only to build (compilers, SDKs, dev packages) versus what is needed to run (the binary or bundle, plus a minimal runtime). Only the second set belongs in the final image.

2. Define a build stage

Name the stage with AS:

FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/server

3. Define a slim runtime stage

Start a fresh, minimal base for the final image:

FROM alpine:3.20
RUN adduser -D appuser
USER appuser

For a static binary you can use an even smaller base later.

4. Copy artifacts between stages

Use COPY --from to pull the built artifact into the runtime stage:

COPY --from=build /out/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]

The full Dockerfile combines all stages in order.

5. Build a specific target

During development you can stop at an intermediate stage:

docker build --target build -t myapp:build .

This is useful for running tests inside the build stage without producing the runtime image.

6. Compare final image sizes

Build the full image and inspect:

docker build -t myapp:multi .
docker images myapp

The multi-stage image typically drops from hundreds of megabytes to tens.

Verification

Run docker run --rm myapp:multi. The application should start without any build tools present. Confirm with docker run --rm myapp:multi sh -c 'which go || echo no-go' that the Go toolchain is absent from the final image.

Next Steps

Replace the runtime base with a distroless image for the smallest footprint. Cache the dependency-download stage in CI to speed up builds, and add a test stage that fails the build when tests fail.

Prerequisites

  • Familiarity with Dockerfiles
  • A compiled or bundled application

Steps

  • 1
    Identify build vs runtime needs
  • 2
    Define a build stage
  • 3
    Define a slim runtime stage
  • 4
    Copy artifacts between stages
  • 5
    Build a specific target
  • 6
    Compare final image sizes

Category

Containers