Multi-Stage Docker Builds and Building for Multiple Architectures

Large container images mean slower deployments and a bigger attack surface. This article explains how multi-stage builds keep production images lean, and how Buildx and BuildKit make it possible to build images for multiple CPU architectures at once.

Multi-Stage BuildBuildxBuildKit

~4 min read · Updated Sep 5, 2026

Introduction

When it comes to container images, smaller is almost always better — smaller images are faster to pull, carry fewer potential vulnerabilities, and present a reduced attack surface. Multi-stage builds are the primary technique Docker offers for keeping production images lean.

How Multi-Stage Builds Work

A multi-stage build uses a single Dockerfile with multiple FROM instructions, each representing a distinct build stage. This allows heavy build tools and compilers to live in an early, larger stage, while only the compiled output gets copied into a minimal final image intended for production — often called a slim image.

FROM golang:1.23.4-alpine AS base
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .

FROM base AS build-client
RUN go build -o /bin/client ./cmd/client

FROM base AS build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch AS prod
COPY --from=build-client /bin/client /bin/
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]

Each named stage produces an intermediate image that later stages can reference, but Docker discards these intermediate images once the final stage completes. Stages without dependencies on each other, such as build-client and build-server in this example, can run in parallel, speeding up the overall build.

The final production image built this way ends up dramatically smaller than the build-time base image, since it contains only the compiled binaries and nothing else.

Building Multiple Images from One Dockerfile

A single Dockerfile can also produce multiple distinct images by splitting the final stage and using the --target flag to choose which one to build:

docker build -t multi:client --target prod-client -f Dockerfile-final .
docker build -t multi:server --target prod-server -f Dockerfile-final .

Buildx and BuildKit

Docker's build system is split into a client and a server: Buildx is the client, implemented as a CLI plugin, and BuildKit is the server that actually performs builds. Since Docker v23.0, Buildx has been the default build client for every docker build command.

Buildx can be configured to talk to multiple BuildKit instances, called builders, which can run locally, in a private cloud, or on Docker's own Build Cloud service. Local builders typically use the docker-container driver, while remote builds use the cloud driver.

docker buildx ls

Multi-Architecture Builds

Docker can build images for CPU architectures different from the local machine — for example, building ARM images on an AMD system. This is done with the --platform flag:

docker buildx build --builder=container \
  --platform=linux/amd64,linux/arm64 \
  -t nigelpoulton/ddd-book:ch8.1 --push .

Each Dockerfile instruction executes once per target architecture, and the resulting images are published under the same tag, letting a single docker pull automatically fetch the correct version for any platform.

Good Practices

Two practices meaningfully improve build efficiency and image size. First, structuring a Dockerfile so that instructions most likely to change come later helps preserve the build cache, since a cache miss on one instruction invalidates the cache for everything after it. Second, installing only essential packages — using flags like no-install-recommends where available — can dramatically reduce the number of dependencies pulled into an image.

Conclusion

Multi-stage builds separate the tools needed to build an application from what's needed to run it, resulting in smaller, safer production images. Combined with Buildx and BuildKit's ability to build for multiple architectures and leverage shared caching, these techniques form the foundation of efficient, production-ready container workflows.

Written & researched by Dr. Shahin Siami

Related Articles

How Docker Uses Linux Security Technologies: Namespaces, Cgroups, and More

Docker's security model is built on layered defense, combining well-established Linux kernel technologies with sensible defaults out of the box. This article explains how namespaces, control groups, capabilities, mandatory access control, and seccomp work together to isolate and secure containers.

Continue

Managing Persistent Data in Docker with Volumes

Containers get a temporary writable layer by default, which disappears the moment a container is deleted — fine for scratch data, but risky for anything worth keeping. This article explains how Docker volumes decouple persistent data from container lifecycles, and walks through creating, using, and sharing them safely.

Continue

How Docker Overlay Networks Work: VXLAN Under the Hood

Overlay networks let containers on different hosts communicate as if they shared a single flat network, forming the backbone of most cloud-native microservices apps. This article covers how to build, encrypt, and test a Docker overlay network across a Swarm cluster, and explains the VXLAN tunneling technology powering it behind the scenes.

Continue

Connecting Docker Containers to VLANs and Load Balancing with Swarm

Beyond basic bridge networks, Docker can connect containers directly to existing physical VLANs, resolve service names automatically, and distribute traffic across a Swarm cluster. This article covers the macvlan driver, Docker's built-in service discovery, and Swarm's ingress load balancing mesh.

Continue

Understanding Docker Networking: CNM, Libnetwork, and Bridge Networks

Docker networking is built on an open design called the Container Network Model, implemented through libnetwork and extended by pluggable drivers. This article covers the theory behind Docker networking and walks through creating and testing single-host bridge networks, including name resolution and port mapping.

Continue

Deploying and Managing Multi-Node Clusters with Docker Swarm

Docker Swarm turns a group of Docker nodes into a secure, highly available cluster with built-in application orchestration. This article covers building a multi-node swarm, deploying a microservices app declaratively, and performing rolling updates without losing desired-state consistency.

Continue