Want a set of commands you can copy/paste? Jump to the TLDR; at the bottom.
I jumped on the Apple Silicon band wagon as soon as I heard how awesome they were and I was not disappointed. My Apple Silicon MacBook Air is now my daily driver that I use for work as a software engineer and for personal projects.
I extensively use Docker in the projects I work on, so this led to a unique problem. When I build a Docker image on my Mac – it builds an ARM version (specifically arm64). This means this image can’t run on any other device like a raspberry pi (linux/arm/v7) or a typical server (linux/amd64) as the application binaries inside are not compatible.
Fortunately, Docker has supported cross CPU architecture builds for a while now through an experimental feature called
buildx. It’s a CLI plugin that integrates the Moby BuildKit toolkit. This allows you to build a Docker Image for a variety of different CPU architectures and it uses QEMU under the hood to do the emulation.
How to build a multi-architecture Docker Image on Apple Silicon
This guide assumes you have an Apple Silicon equipped Mac running macOS Big Sur. It was written with an Apple M1 equipped MacBook Air so results may vary across devices.
Step 1: enable experimental Docker Desktop features
buildx feature is currently “experimental” so we need to enable Docker Desktop’s experimental feature support.
To do so, open up Docker Desktop then navigate to Preferences.
Once you’re there, select “Experimental Features” and toggle the slider to on. Click on “Apply & Restart” to save the changes and restart the Docker daemon.
Once you’ve enabled experimental features, you can close the Docker Desktop preferences. In your terminal, open the folder that contains the Dockerfile you wish to build for multiple architectures. Run the
docker buildx ls command to list the current builder instances. You should see something similar to below.
$ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS default * docker default default running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
Next create a new builder instance with
docker buildx create --use so we can perform multiple builds in parallel. Without this step, you’ll have to use the default Docker one which only supports a single platform per build. You’ll see it created if you run
docker buildx ls again.
$ docker buildx lsNAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS reverent_banach * docker-container reverent_banach0 unix:///var/run/docker.sock running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6 default docker default default running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
Now you can use
buildx like below to start a multi-architecture build. You’ll have to push it straight to a registry (either the public or a private one) with
--push if you want Docker to automatically manage the multi-architecture manifest for you. Don’t forget to tag it (the example is using the open source MemberMatters software I wrote) and add a list of all the platforms that you wish to build for. You can see the compatible platforms from the previous
docker buildx ls command. The command below will build an image for both Apple Silicon Macs (
linux/arm64), and standard x86 platforms (
$ docker buildx build --platform linux/amd64,linux/arm64 --push -t membermatters/membermatters .
The first time you run a build, you’ll have to wait for the Moby BuildKit image to download so you’ll see something like this.
$ docker buildx build --platform linux/amd64,linux/arm64 --push -t membermatters/membermatters . [+] Building 16.5s (7/43) => [internal] booting buildkit 10.4s => => pulling image moby/buildkit:buildx-stable-1 8.1s => => creating container buildx_buildkit_admiring_shirley0
Once the build is finished, it will be automatically uploaded to your configured registry. Docker will also automatically manage the manifest list for you. This allows Docker to combine the separate builds for each architecture into a single “manifest”. This means users can do a normal
docker pull <image> and the Docker client will automatically work out the correct image for their CPU architecture – pretty neat!
- Open the Docker Desktop dashboard then open up Preferences (cog icon). Go to “Experimental Features” then turn it on and apply it.
- Next create a new builder instance with
docker buildx create --use. This lets you specify multiple docker platforms at once.
- To build your Dockerfile for typical x86 systems and Apple Silicon Macs, run
docker buildx build --platform linux/amd64,linux/arm64 --push -t <tag_to_push> .
- Done. Please note that you have to push directly to a repository if you want Docker Desktop to automatically manage the manifest list for you (this is probably something you want). Read the paragraph above to find out why. 😉