Multi-platform builds
Page options
A multi-platform build refers to a single build invocation that targetsmultiple different operating system or CPU architecture combinations. Whenbuilding images, this lets you create a single image that can run on multipleplatforms, such aslinux/amd64
,linux/arm64
, andwindows/amd64
.
Why multi-platform builds?
Docker solves the "it works on my machine" problem by packaging applicationsand their dependencies into containers. This makes it easy to run the sameapplication on different environments, such as development, testing, andproduction.
But containerization by itself only solves part of the problem. Containersshare the host kernel, which means that the code that's running inside thecontainer must be compatible with the host's architecture. This is why youcan't run alinux/amd64
container on an arm64 host (without using emulation),or a Windows container on a Linux host.
Multi-platform builds solve this problem by packaging multiple variants of thesame application into a single image. This enables you to run the same image ondifferent types of hardware, such as development machines running x86-64 orARM-based Amazon EC2 instances in the cloud, without the need for emulation.
Difference between single-platform and multi-platform images
Multi-platform images have a different structure than single-platform images.Single-platform images contain a single manifest that points to a singleconfiguration and a single set of layers. Multi-platform images contain amanifest list, pointing to multiple manifests, each of which points to adifferent configuration and set of layers.
When you push a multi-platform image to a registry, the registry stores themanifest list and all the individual manifests. When you pull the image, theregistry returns the manifest list, and Docker automatically selects thecorrect variant based on the host's architecture. For example, if you run amulti-platform image on an ARM-based Raspberry Pi, Docker selects thelinux/arm64
variant. If you run the same image on an x86-64 laptop, Dockerselects thelinux/amd64
variant (if you're using Linux containers).
Prerequisites
To build multi-platform images, you first need to make sure that your Dockerenvironment is set up to support it. There are two ways you can do that:
- You can switch from the "classic" image store to the containerd image store.
- You can create and use a custom builder.
The "classic" image store of the Docker Engine does not support multi-platformimages. Switching to the containerd image store ensures that your Docker Enginecan push, pull, and build multi-platform images.
Creating a custom builder that uses a driver with multi-platform support,such as thedocker-container
driver, will let you build multi-platform imageswithout switching to a different image store. However, you still won't be ableto load the multi-platform images you build into your Docker Engine imagestore. But you can push them to a container registry directly withdocker build --push
.
The steps for enabling the containerd image store depends on whether you'reusing Docker Desktop or Docker Engine standalone:
If you're using Docker Desktop, enable the containerd image store in theDocker Desktop settings.
If you're using Docker Engine standalone, enable the containerd image storeusing thedaemon configuration file.
To create a custom builder, use thedocker buildx create
command to create abuilder that uses thedocker-container
driver.
$ docker buildx create\ --name container-builder \ --driver docker-container \ --bootstrap --use
NoteBuilds with the
docker-container
driver aren't automatically loaded to yourDocker Engine image store. For more information, seeBuilddrivers.
If you're using Docker Engine standalone and you need to build multi-platformimages using emulation, you also need to install QEMU, seeInstall QEMUmanually.
Build multi-platform images
When triggering a build, use the--platform
flag to define the targetplatforms for the build output, such aslinux/amd64
andlinux/arm64
:
$ docker buildx build --platform linux/amd64,linux/arm64 .
Strategies
You can build multi-platform images using three different strategies,depending on your use case:
- Using emulation, viaQEMU
- Use a builder withmultiple native nodes
- Usecross-compilation with multi-stage builds
QEMU
Building multi-platform images under emulation with QEMU is the easiest way toget started if your builder already supports it. Using emulation requires nochanges to your Dockerfile, and BuildKit automatically detects thearchitectures that are available for emulation.
NoteEmulation with QEMU can be much slower than native builds, especially forcompute-heavy tasks like compilation and compression or decompression.
Usemultiple native nodes orcross-compilation instead, if possible.
Docker Desktop supports running and building multi-platform images underemulation by default. No configuration is necessary as the builder uses theQEMU that's bundled within the Docker Desktop VM.
Install QEMU manually
If you're using a builder outside of Docker Desktop, such as if you're usingDocker Engine on Linux, or a custom remote builder, you need to install QEMUand register the executable types on the host OS. The prerequisites forinstalling QEMU are:
- Linux kernel version 4.8 or later
binfmt-support
version 2.1.7 or later- The QEMU binaries must be statically compiled and registered with the
fix_binary
flag
Use thetonistiigi/binfmt
image toinstall QEMU and register the executable types on the host with a singlecommand:
$ docker run --privileged --rm tonistiigi/binfmt --install all
This installs the QEMU binaries and registers them withbinfmt_misc
, enabling QEMU toexecute non-native file formats for emulation.
Once QEMU is installed and the executable types are registered on the host OS,they work transparently inside containers. You can verify your registration bychecking ifF
is among the flags in/proc/sys/fs/binfmt_misc/qemu-*
.
Multiple native nodes
Using multiple native nodes provide better support for more complicated casesthat QEMU can't handle, and also provides better performance.
You can add additional nodes to a builder using the--append
flag.
The following command creates a multi-node builder from Docker contexts namednode-amd64
andnode-arm64
. This example assumes that you've already addedthose contexts.
$ docker buildx create --use --name mybuild node-amd64mybuild$ docker buildx create --append --name mybuild node-arm64$ docker buildx build --platform linux/amd64,linux/arm64 .
While this approach has advantages over emulation, managing multi-node buildersintroduces some overhead of setting up and managing builder clusters.Alternatively, you can use Docker Build Cloud, a service that provides managedmulti-node builders on Docker's infrastructure. With Docker Build Cloud, youget native multi-platform ARM and X86 builders without the burden ofmaintaining them. Using cloud builders also provides additional benefits, suchas a shared build cache.
After signing up for Docker Build Cloud, add the builder to your localenvironment and start building.
$ docker buildx create --driver cloud <ORG>/<BUILDER_NAME>cloud-<ORG>-<BUILDER_NAME>$ docker build\ --builder cloud-<ORG>-<BUILDER_NAME> \ --platform linux/amd64,linux/arm64,linux/arm/v7 \ --tag <IMAGE_NAME> \ --push .
For more information, seeDocker Build Cloud.
Cross-compilation
Depending on your project, if the programming language you use has good supportfor cross-compilation, you can leverage multi-stage builds to build binariesfor target platforms from the native architecture of the builder. Special buildarguments, such asBUILDPLATFORM
andTARGETPLATFORM
, are automaticallyavailable for use in your Dockerfile.
In the following example, theFROM
instruction is pinned to the nativeplatform of the builder (using the--platform=$BUILDPLATFORM
option) toprevent emulation from kicking in. Then the pre-defined$BUILDPLATFORM
and$TARGETPLATFORM
build arguments are interpolated in aRUN
instruction. Inthis case, the values are just printed to stdout withecho
, but thisillustrates how you would pass them to the compiler for cross-compilation.
# syntax=docker/dockerfile:1FROM --platform=$BUILDPLATFORM golang:alpine AS buildARG TARGETPLATFORMARG BUILDPLATFORMRUNecho"I am running on$BUILDPLATFORM, building for$TARGETPLATFORM" > /logFROM alpineCOPY --from=build /log /log
Examples
Here are some examples of multi-platform builds:
- Simple multi-platform build using emulation
- Multi-platform Neovim build using Docker Build Cloud
- Cross-compiling a Go application
Simple multi-platform build using emulation
This example demonstrates how to build a simple multi-platform image usingemulation with QEMU. The image contains a single file that prints thearchitecture of the container.
Prerequisites:
- Docker Desktop, or Docker Engine withQEMU installed
- containerd image store enabled
Steps:
Create an empty directory and navigate to it:
$ mkdir multi-platform$cd multi-platform
Create a simple Dockerfile that prints the architecture of the container:
# syntax=docker/dockerfile:1FROM alpineRUN uname -m > /arch
Build the image for
linux/amd64
andlinux/arm64
:$ docker build --platform linux/amd64,linux/arm64 -t multi-platform .
Run the image and print the architecture:
$ docker run --rm multi-platform cat /arch
- If you're running on an x86-64 machine, you should see
x86_64
. - If you're running on an ARM machine, you should see
aarch64
.
- If you're running on an x86-64 machine, you should see
Multi-platform Neovim build using Docker Build Cloud
This example demonstrates how run a multi-platform build using Docker BuildCloud to compile and exportNeovim binariesfor thelinux/amd64
andlinux/arm64
platforms.
Docker Build Cloud provides managed multi-node builders that support nativemulti-platform builds without the need for emulation, making it much faster todo CPU-intensive tasks like compilation.
Prerequisites:
Steps:
Create an empty directory and navigate to it:
$ mkdir docker-build-neovim$cd docker-build-neovim
Create a Dockerfile that builds Neovim.
# syntax=docker/dockerfile:1FROM debian:bookworm AS buildWORKDIR /workRUN --mount=type=cache,target=/var/cache/apt,sharing=locked\ --mount=type=cache,target=/var/lib/apt,sharing=locked\ apt-get update&& apt-get install -y\ build-essential\ cmake\ curl\ gettext\ ninja-build\ unzipADD https://github.com/neovim/neovim.git#stable .RUN makeCMAKE_BUILD_TYPE=RelWithDebInfoFROM scratchCOPY --from=build /work/build/bin/nvim /
Build the image for
linux/amd64
andlinux/arm64
using Docker Build Cloud:$ docker build\ --builder <cloud-builder> \ --platform linux/amd64,linux/arm64 \ --output ./bin .
This command builds the image using the cloud builder and exports thebinaries to the
bin
directory.Verify that the binaries are built for both platforms. You should see the
nvim
binary for bothlinux/amd64
andlinux/arm64
.$ tree ./bin./bin├── linux_amd64│ └── nvim└── linux_arm64 └── nvim3 directories, 2 files
Cross-compiling a Go application
This example demonstrates how to cross-compile a Go application for multipleplatforms using multi-stage builds. The application is a simple HTTP serverthat listens on port 8080 and returns the architecture of the container.This example uses Go, but the same principles apply to other programminglanguages that support cross-compilation.
Cross-compilation with Docker builds works by leveraging a series ofpre-defined (in BuildKit) build arguments that give you information aboutplatforms of the builder and the build targets. You can use these pre-definedarguments to pass the platform information to the compiler.
In Go, you can use theGOOS
andGOARCH
environment variables to specify thetarget platform to build for.
Prerequisites:
- Docker Desktop or Docker Engine
Steps:
Create an empty directory and navigate to it:
$ mkdir go-server$cd go-server
Create a base Dockerfile that builds the Go application:
# syntax=docker/dockerfile:1FROM golang:alpine AS buildWORKDIR /appADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .RUN go build -o server .FROM alpineCOPY --from=build /app/server /serverENTRYPOINT["/server"]
This Dockerfile can't build multi-platform with cross-compilation yet. Ifyou were to try to build this Dockerfile with
docker build
, the builderwould attempt to use emulation to build the image for the specifiedplatforms.To add cross-compilation support, update the Dockerfile to use thepre-defined
BUILDPLATFORM
andTARGETPLATFORM
build arguments. Thesearguments are automatically available in the Dockerfile when you use the--platform
flag withdocker build
.- Pin the
golang
image to the platform of the builder using the--platform=$BUILDPLATFORM
option. - Add
ARG
instructions for the Go compilation stages to make theTARGETOS
andTARGETARCH
build arguments available to the commands inthis stage. - Set the
GOOS
andGOARCH
environment variables to the values ofTARGETOS
andTARGETARCH
. The Go compiler uses these variables to docross-compilation.
# syntax=docker/dockerfile:1FROM --platform=$BUILDPLATFORM golang:alpine AS buildARG TARGETOSARG TARGETARCHWORKDIR /appADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .RUNGOOS=${TARGETOS}GOARCH=${TARGETARCH} go build -o server .FROM alpineCOPY --from=build /app/server /serverENTRYPOINT["/server"]
# syntax=docker/dockerfile:1FROM golang:alpine AS buildWORKDIR /appADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .RUN go build -o server .FROM alpineCOPY --from=build /app/server /serverENTRYPOINT["/server"]
# syntax=docker/dockerfile:1-FROM golang:alpine AS build+FROM --platform=$BUILDPLATFORM golang:alpine AS build+ARG TARGETOS+ARG TARGETARCHWORKDIR /appADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .-RUN go build -o server .+RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server .FROM alpineCOPY --from=build /app/server /serverENTRYPOINT ["/server"]
- Pin the
Build the image for
linux/amd64
andlinux/arm64
:$ docker build --platform linux/amd64,linux/arm64 -t go-server .
This example has shown how to cross-compile a Go application for multipleplatforms with Docker builds. The specific steps on how to do cross-compilationmay vary depending on the programming language you're using. Consult thedocumentation for your programming language to learn more about cross-compilingfor different platforms.
TipYou may also want to consider checking outxx - Dockerfile cross-compilation helpers.
xx
is a Docker image containing utility scripts that make cross-compiling with Docker builds easier.