Docker Multi-Stage Builds: Reducing Image Size

Multi-stage builds let you use one image for building/compiling and a separate, much smaller image for actually running your application — dramatically reducing final image size by excluding build-time-only dependencies.

The Problem Multi-Stage Builds Solve

Compiling/building an application often requires tools (compilers, build dependencies) that aren't needed at runtime — without multi-stage builds, these unnecessary tools remain in your final image, increasing size and attack surface for no runtime benefit.

Single-Stage Build (The Problem)

FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o myapp
CMD ["./myapp"]

The final image includes the entire Go compiler toolchain, even though the compiled binary itself doesn't need it to run — unnecessarily large.

Multi-Stage Build (The Solution)

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]

The first stage (builder) compiles the application using the full Go toolchain; the second stage starts fresh from a minimal Alpine base and copies only the compiled binary — the final image excludes the entire Go compiler and build tools.

Real-World Size Impact

A Go application built this way might result in a final image of just a few MB, compared to potentially hundreds of MB or more for a naive single-stage build including the full compiler toolchain — a substantial, meaningful difference.

Multi-Stage Builds for Node.js/JavaScript Applications

FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
CMD ["node", "dist/server.js"]

Using Multiple Named Stages

FROM node:20 AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20 AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/server.js"]

More than two stages can further optimize caching — separating dependency installation from the build step means dependency changes don't invalidate the build stage's cache unnecessarily.

Benefits Beyond Just Size

  • Smaller attack surface — fewer packages/tools in the final image means fewer potential vulnerabilities
  • Faster deployment — smaller images transfer and start faster
  • Cleaner separation between build-time and runtime dependencies

Verifying Image Size Reduction

docker images myapp

Compare sizes before and after implementing multi-stage builds to confirm the actual improvement for your specific application.

Common Errors

Runtime error about a missing dependency — verify you've copied everything the runtime actually needs from the build stage (compiled output, but also any runtime dependencies like node_modules for non-compiled languages).

Continue Reading

Browse more articles in Docker & Containers.

  • docker multi-stage build, reduce docker image size, dockerfile optimization, docker build stages
  • 0 istifadəçi bunu faydalı hesab edir
Bu cavab sizə kömək etdi?

Uyğun məqalələr

How to Install Docker Engine on Ubuntu & Debian (Complete Guide)

Docker lets you package applications and their dependencies into lightweight, isolated containers...

How to Install Docker Compose on Ubuntu & Debian

Docker Compose lets you define and run multi-container applications from a single YAML file,...

Deploy Your First Docker Container: A Beginner's Walkthrough

With Docker installed, this hands-on walkthrough covers the core workflow you'll use constantly:...

How to Update Docker and Docker Compose Safely

Keeping Docker Engine and Compose updated brings security fixes, performance improvements, and...

Docker Compose Troubleshooting: Common Errors & Fixes

A reference guide to the Docker and Docker Compose errors you're most likely to encounter, with...