Multi-Architecture Build Container Images

Posted on Apr 3, 2025

It used to be that you could get away with building your container images with only amd64 in mind. However, with the adoption and growth of arm-based devices we can no longer focus only on a single architecture. Luckily building cross architecture images requires little to no preparation. Both Docker and Podman provide a solution to this problem.

Requirements

For Docker and Docker Desktop, you’ll need to ensure that you’re using containerd. For the most part, if you’re on Linux then you’ll be using containerd already.

Docker Desktop

According to the Docker documentation, Docker Desktop has been transitioning to contianerd image store. To verify that Docker Desktop is using containerd

  1. Open Docker Desktop
  2. Navigate to Settings in Docker Desktop.
  3. In the General tab, check Use containerd for pulling and storing images.
  4. Select Apply.

Docker Engine

For Docker Engine add the following to /etc/docker/daemon.json


    
{
  "features": {
    "containerd-snapshotter": true
  }
}

Podman

There isn’t anything that you need to do.

Build Strategies

There are three strategies for building images for other architectures, QEMU, native nodes aka cross-platform, and cross-compilation. The simpletest way is using emulation through QEMU. There is no additional configuration that is needed when using Docker Desktop because Docker Desktop uses a VM that already has QEMU configured.

QEMU

For Docker Engine you’ll need a few things first.

  1. Kernel 4.8 or later
  2. binfmt-support 2.17 package installed
  3. QEMU binaries must be statically compiled and registered with the fix_binary flag

Once the requirements have been met, you can install QEMU inside a container:



docker run --privileged --rm tonistiigi/binfmt --install all

Cross-platform

Cross-platform building is best for when you are building an image that’s more complicated, requires better performance, or cross-compiling isn’t an option. For this strategy we’ll need to have different contexts for different architectures. Contexts are a way for users to manage multiple Docker daemons from a single host.



docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock   
docker context create amd64-node --docker host=ssh://paulus@amd64-node
amd64-node
Successfully created context "amd64-node"
docker buildx create --use --name amd64-builder amd64-node

Cross-compiling

Cross-compiling is another option that you can use to build different binaries based on the architecture within the container. When using this method you can use the BUILDPLATFORM and TARGETPLATFORM variables within your Dockerfile.


    
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server .

FROM alpine
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]

In the example above, go is compiling the program for a $TARGETOS and $TARGETPLATFORM within the build process.

Process

When building multi-architecture container images the basic process is as follows.

  1. Create a Dockerfile for each architecture you wish to support.
  2. Build the image using the build or buildx build sub-commands for each of the target architectures by specifying the appropriate --platform option.
  3. Tag the images.
  4. Push the images
  5. Create a manifest list that references the different architecture-specific images.
  6. Push the manifest

QEMU with Docker



vim Dockerfile
docker buildx build --platform linux/amd64,linux/arm64 --output "type=image,push=true" --tag registry.paulslinuxbox.net/myapp:tag .

If there are no existing builders then you can create one by running docker builder create --use ---name docker_builder.

Cross-platform with Docker



vim Dockerfile
docker context create amd64-node --docker host=tcp://amd64-node:2375
docker context create arm64-node --docker host=tcp://arm64-node:2375
docker buildx create --use --name multi-arch amd64-node
docker buildx create --apend --name multi-arch arm64-node
docker buildx build --platform linux/amd64,linux/arm64 --output "type=image,push=true" --tag registry.paulslinuxbox.net/myapp:tag

Podman



vim Dockerfile
podman system connection add amd64-node --identity ~/.ssh/id_rsa ssh://192.168.1.2/path/to/podman.sock
podman system connection default amd64-node
podman manifest create myapp
podman build --platform linux/amd64,linux/arm64 --manifest myapp --output "type=image,push=true" --tag registry.paulslinuxbox.net/myapp:tag .
podman manifest push myapp:tag

Conclusion

That concludes building images for different architectures. As you can see creating container images for different architectures isn’t difficult. Although you can use QEMU without issues, if possible build the container image on the native architecture.