Categories
Docker Software

How to build x86 (and others!) Docker images on an M1 Mac

TLDR;

Want a set of commands you can copy/paste? Jump to the TLDR; at the bottom.

Background

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

The Docker 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.

If you can’t see an “Experimental Features” option, you may have to sign up for the Docker developer program at this link. I suspect itโ€™s a new thing which is why only recently created accounts seem to need to sign up.

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 (linux/amd64).

$ 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!

TLDR; Version

  1. Open the Docker Desktop dashboard then open up Preferences (cog icon). Go to “Experimental Features” then turn it on and apply it.
  2. Next create a new builder instance with docker buildx create --use. This lets you specify multiple docker platforms at once.
  3. 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> .
  4. 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. ๐Ÿ˜‰

35 replies on “How to build x86 (and others!) Docker images on an M1 Mac”

Good day,
Thank you for your article.
Is it possible to run the image on mac m1 chip jfloff / glassfish-2.1.1: 7 (linux / amd64)?
Or a general type image (linux / amd64)?
If so, how?
Thank you, have a nice day.

Not sure what you’re really asking sorry! You should be able to run any compatible docker image on your M1 chip just like any other architecture. If the Docker image doesn’t have an ARM64 version you may have to build it yourself using the instructions in this post.

Docker on M1 can *sort* of run linux/amd64 images , but its very unreliable and prone to exploding in flames at random intervals. So yes, but dont use it in production, and dont be surprised if your forced to build yourself an arm container if it just refuses to run sanely

Thanks – worked like a treat, spent the best part of two days trying to work out why my containers on AWS ECS were stopping – turned out it was that the arm64 wouldn’t run on their default amd architecture so your article was very handy indeed.

same here! And the fact that it crashed really early meant that there were no logs other than ‘Exit code 1’ which made debugging nearly impossible.

It works on a Docker for desktop 3.3.3 by default, no need to enable experimental features.

I get the following error on my 2020 M1 Macbook Air ๐Ÿ™
—————————-
[acbp@acmba:~/data/built/acpytest] docker –version
Docker version 20.10.7, build f0df35096d
[acbp@acmba:~/data/built/acpytest] 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/386, linux/arm/v7, linux/arm/v6
[acbp@acmba:~/data/built/acpytest] docker buildx build –platform linux/amd64 –push -t my_dockerhub_org/actest:0.0.13-test1
error: “docker buildx build” requires exactly 1 argument.
See ‘docker buildx build –help’.
Usage: docker buildx build [OPTIONS] PATH | URL | –
Start a build

I’ve got one question. I was able to accomplish the same steps just by using the –platform on docker build rather than using the docker buildx build command. I was also able to run the generated image and ran uname -a to verify it is a x86 arch build.

My question is: What’s the difference between this approach and using buildx? Only thing I can tell is that buildx uses a container to build the images and stores them there rather than on the host machine. I had to add the –load flag in order to run the generated image on my machine.

I’m not entirely sure sorry! It may have something to do with the number of simultaneous builds from memory, or it could just be a “newer” way of doing the same thing. ๐Ÿ™‚

Curious to know if you figured out the answer to this question? I’m also wondering the same thing currently. The documentation docker provides on this seems light.

Hi i got this error can you help me.
[+] Building 20.6s (3/3) FINISHED
=> [internal] booting buildkit 19.3s
=> => pulling image moby/buildkit:buildx-stable-1 17.2s
=> => creating container buildx_buildkit_sweet_elgamal0 2.1s
=> ERROR [internal] load build definition from Dockerfile 1.0s
=> => transferring dockerfile: 40B 0.1s
=> ERROR [internal] load .dockerignore 1.1s
=> => transferring context: 40B 0.0s
——
> [internal] load build definition from Dockerfile:
——
——
> [internal] load .dockerignore:
——
error: failed to solve: rpc error: code = Unknown desc = failed to read dockerfile: error from sender: open .Trash: operation not permitted

Unfortunately, I still fall into this issue, even after doing all of the above. The case:
– host machine is M1 Pro MacBook
– trying to compile x86 rust application inside docker container
– no arm64 image equivalents in registry
– getting segfault from qemu on compiling anyhow library dependency
– segfault happens on `docker buildx build –platform linux/amd64,linux/arm64 –push –tag some_tag:latest .
– tried running containers with FROM –platform=linux/amd64, results in segfault

Do you guys have a solution for this kind of issue? Any help would be amazing ๐Ÿ™‚

Hey guys, I cannot handle the following error message after following command:

-> docker buildx build –platform linux/amd64,linux/arm64 –push -t membermatters/membermatters .

i get this error=>
[+] Building 2.7s (3/3) FINISHED
=> [internal] booting buildkit 2.2s
=> => pulling image moby/buildkit:buildx-stable-1 1.6s
=> => creating container buildx_buildkit_objective_villani0 0.6s
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 2B 0.1s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.1s

error: failed to solve: failed to read dockerfile: open /tmp/buildkit-mount328837120/Dockerfile: no such file or directory

do you have any idea, i googled a bit and the suggestion is renaming the dockerfile to DockerFile… buuuut i am such a talent and not able to find the file to rename it… sryyy not a pro… i wanted to install this to use ms sql 19 on my mac air 1 ๐Ÿ™‚

It did not work for me.

udo docker buildx build –platform linux/amd64,linux/arm64 –push -t qnap-runner ./
[+] Building 0.3s (2/3)
[+] Building 0.3s (3/3) FINISHED
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.39kB 0.0s
=> ERROR [linux/amd64 internal] load metadata for docker.io/library/ubuntu:latest 0.3s
——
> [linux/amd64 internal] load metadata for docker.io/library/ubuntu:latest:

If you use your own registry with the custom certificate and get this error during `docker buildx build …` command you need to specify the CA certificates file when creating buildx builder. First create the file `buildkit.toml`

“`
[registry.”IP_ADDRESS:5000″]
http = false
insecure = false
ca=[“/path/to/ca-certificates.crt”]
“`
On my mac I have them in `/etc/docker/certs.d/IP_ADDRESS:5000/ca.crt` where I’ve copied them from the registry (`/etc/docker/tls/ca.pem`).

Then use this command to create buildx builder:

“`
docker buildx create –use –config buildkit.toml
“`

Thank you! I also work on an M1Air and have been avoiding docker because I did not know I could cross-arch build.

Leave a Reply

Your email address will not be published.