April 30, 2019·6 min read·engineeringdockerbuildkitarm

Buildx makes the target architecture an explicit build input

Docker's Buildx preview combines BuildKit, emulation and manifest lists. A single build invocation can produce separate architecture-specific images under one registry tag.

A portable container image still contains machine instructions for a particular processor. Moving a Dockerfile between an Intel laptop and an Arm server does not make the executable bytes interchangeable. Docker's April 30 multi-architecture build preview addresses the work of producing and publishing the appropriate images together. Buildx can request several target platforms in one invocation and publish a manifest list that associates a shared image name with those separate results.

The announcement follows Docker's April 24 partnership announcement with Arm. Its immediate developer-facing delivery is a technology preview in Docker Desktop Edge, with version 2.0.4.0, build 33772 or later, named as the starting point. The underlying arrangement combines BuildKit with the emulation and Linux environment supplied by Desktop. The result changes how a developer requests build outputs without requiring a separate physical machine for every architecture in the demonstration.

I see Docker's main contribution in the repeatable build file and the image artifact it produces. Buildx extends that model by making the intended platform explicit. The same source description can produce several artifacts, and the registry can retain the relationship between them.

Desktop supplies the execution environment

Docker Desktop runs a lightweight Linux environment using LinuxKit and the host operating system's virtualization facilities. The preview announcement explains that this environment includes QEMU and a configured binfmt_misc arrangement. That combination lets the Linux environment execute binaries for supported non-native architectures through an emulator.

This matters for an ordinary Dockerfile that runs package installation commands. A target-platform base image contains target-platform executables. When a build executes one of those programs on a machine with another processor architecture, changing a metadata field cannot make the program run. The emulator supplies the execution mechanism that allows the demonstrated Dockerfile to proceed.

The announcement demonstrates a Dockerfile based on Ubuntu that installs curl. Buildx builds it for Linux on amd64, arm64 and arm/v7. Those are separate outputs containing architecture-appropriate image contents. Emulation allows the build steps for the foreign architectures to execute on the developer machine.

The Buildx README as it exists at the preview describes automatic detection of secondary architectures supported by the builder. When BuildKit needs a foreign executable, it uses the program registered with the kernel's binary-format handler. This explains why the announcement can demonstrate an unchanged Dockerfile while still producing different machine code in the resulting images.

That does not imply that arbitrary application source is architecture-independent. The mechanism supplies a way to execute target tools and use target images. An application's dependency availability and assumptions remain part of whether its build succeeds. The preview removes a specific infrastructure obstacle, the need to execute every target instruction directly on a matching local processor.

A builder instance has capabilities and state

The preview introduces explicit builder instances. Its demonstration first lists the default builder, then creates a named builder and selects it. The following sequence follows that workflow with a descriptive local name.

docker buildx create --name multiarch
docker buildx use multiarch
docker buildx inspect --bootstrap

The inspection starts the builder when necessary and reports its supported platforms. In the announcement, the selected builder uses the docker-container driver, which starts a BuildKit container. This is a distinct execution arrangement from simply using the BuildKit library integrated into the Docker daemon.

The contemporary README describes both drivers. With the docker driver, builds use the daemon's bundled BuildKit implementation. The docker-container driver starts BuildKit in a container and supports multi-platform builds. Their output behavior differs as well. Images built through the daemon driver enter the local Docker image store by default, while a container-backed build requires an output choice.

That distinction explains an otherwise surprising result when trying the preview. A successful build need not create an image visible in the local docker images listing. The result may have been exported to a registry or another requested destination. Choosing a builder therefore affects both how the build runs and how its result leaves the builder.

The README also describes isolated builder instances as a way to keep build state for different projects or CI uses separate. This gives the name a concrete purpose beyond selecting an architecture. The builder identifies an execution environment with its own nodes and build state, while the target-platform list identifies the requested outputs.

One tag refers to several images

With the container-backed builder selected, the preview's central operation is a multi-platform build with a registry output. This example uses a placeholder Docker Hub namespace that must be replaced with an account authorized to push the repository.

docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t YOUR_NAMESPACE/example:preview \
  --push .

The --platform argument names the outputs. The --push argument exports the images to the registry together with their multi-architecture manifest. In the announcement, inspection shows one manifest list with a separate child manifest for each requested platform. Docker selects the matching image when a machine subsequently pulls and runs the tag.

A manifest list consequently does not make one binary universal. It records which image belongs to which platform. That distinction matters when tracing an artifact. A shared tag identifies the collection, while each listed image has its own digest and platform information. The announcement demonstrates running particular child images by digest and checking their architecture.

The corresponding inspection command is part of the preview as well.

docker buildx imagetools inspect YOUR_NAMESPACE/example:preview

This is registry inspection. It describes what the published name resolves to, which is the relevant question after a --push build. An inspection of only the local image store would address a different destination and could miss the output that the build actually produced.

Emulation is one supported strategy

The contemporary README also describes native nodes and language cross-compilation. Multiple nodes can belong to one builder, allowing each architecture to build on a suitable native machine. The documentation says native nodes generally provide better performance and handle complicated cases that QEMU may not support. That supplies a reason to retain the builder abstraction even when a laptop demonstration works with emulation.

Cross-compilation changes which instructions must execute during the build. A compiler running on the build machine can produce an executable for a different target. The README documents BUILDPLATFORM and TARGETPLATFORM as available build arguments and describes using multi-stage builds for this arrangement. The compiler stage can run on a native platform while the output belongs to another platform.

For the static Go service image I prefer, this separation is an appealing fit. The build stage and the final executable need not have the same architecture. The important requirement is that the final image's platform metadata and its executable agree, regardless of whether the build used an emulator or a cross-compiler to obtain the bytes.

Emulation, native nodes and cross-compilation can therefore share the same output model. Their execution costs and application requirements differ, but Buildx can still describe the target platforms and combine the resulting images into a manifest list. Registry naming no longer needs to expose every detail of the machinery that built each architecture.

Cache output is also explicit

The preview README documents exporting cache to registry, local and inline destinations. Inline cache places cache metadata in the image configuration. Registry cache uses a cache manifest, while local cache uses a directory on the client. These are separate output decisions from the requested processor architectures, although they belong to the same builder configuration.

The documented cache export mode determines whether exported layers include only those in the final build stage or also those in intermediate stages. For a multi-stage build, that distinction matters because an intermediate compiler environment can contain reusable work absent from the final application image. A minimal final image and a useful distributed build cache therefore need not contain the same layers.

This is another consequence of treating the builder as an explicit environment. A registry image output identifies what an application consumer can run. A cache export identifies work another build can reuse. Buildx exposes those choices without requiring the application image to retain every intermediate build dependency.

The preview changes a specific part of portability

Docker explicitly labels Buildx experimental and warns that its flags can change. The announcement describes the x as the experimental designation and discusses eventual integration with the regular build command as an intention. That is a plan stated at the preview, not an established compatibility promise.

The useful delivery today is already concrete. A developer can select a container-backed BuildKit builder, request several platforms, and publish their images together. Consumers retain a single familiar tag, while the registry holds separate manifests with explicit platform identities. The execution strategy remains a builder concern, and each target still receives an image built for its own architecture.