Did you know it is possible to run x86_64 Docker containers on an ARM-based M1 mac? It’s quite staightforward with Multipass and Docker (if you are new to Multipass, check out my Multipass for Docker containers on macOS post from a few months ago).

Things may change come macOS 13, which will allow Running Intel Binaries in Linux VMs with Rosetta!

At time of writing, I’m running Multipass 1.10.1+mac with the pre-built Docker container, which is a VM of Ubuntu 22.04 LTS installed with Docker 20.10.17 build 100c701.

Docker can run containers with x86 emulation when using either the --platform argument or based on the DOCKER_DEFAULT_PLATFORM environment variable.

Testing with Hello World

To test:

  • Start Multipass.app
  • If the Docker container has not been run before, then multipass launch docker followed by multipass alias docker:docker to setup an alias.
  • If the container is already present but stopped, then either run multipass start docker or start it from the menu bar.
  • Next run the hello-world sample with multipass docker -- run --platform amd64 --rm hello-world
  • Make sure hello-world reports that its running the (amd64) image instead of (arm64v8):
docker run --platform amd64 hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64).

Testing with IBM Db2

For another quick test, I thought I’d start up IBM Db2, which is compiled only for amd64, ppc64le, and s390x (Power and Z) but not ARM. FYI, I posted a detailed guide on Running Db2 Data Server Manager in a container a couple of years ago.

The commands in this test are executed in a Shell instance in the container directly, multipass shell docker.

First, trying Db2 without the --platform parameter:

docker run -itd --name db2x86 --privileged=true -p 50000:50000 \
-e LICENSE=accept -e DB2INST1_PASSWORD=1234 -e DBNAME=testdb \
ibmcom/db2

... will give the error:

docker: no matching manifest for linux/arm64/v8 in the manifest list entries.

Next, trying with the --platform parameter... this time, Docker downloads the x86 images and manages to run Db2 properly:

docker run -itd --name db2x86 --privileged=true -p 50000:50000 \
-e LICENSE=accept -e DB2INST1_PASSWORD=1234 -e DBNAME=testdb \
--platform amd64 ibmcom/db2

You can check Db2 intialization progress with docker logs -f db2x86, which should complete successfully with:

(*) All databases are now active. 
(*) Setup has completed.

Troubleshooting

Timeout Starting Docker

If multipass start docker fails with this error:

start failed: The following errors occurred:                                    
docker: timed out waiting for response

Then it might be the “dreaded macOS firewall issue”. You can either try to:

  • Add a Firewall exception to “Allow incoming connections” for Multipass.app, from System Preferences > Security and Privacy > Firewall > Firewall Options
  • Or disable the Firewall altogether, which works reliably for me...

This Issue #2387:Firewall randomly messing with Multipass networking on macOS has more details on this known issue.

Docker HTTP Timeout

If you are getting the error below upon docker run or docker pull:

docker: Error response from daemon: error parsing HTTP 408 
response body: invalid character '<' looking for beginning of value:
"<html><body><h1>408 Request Time-out</h1>\n
Your browser didn't send a complete request in time.\n</body></html>\n".

Then it could be related to the Maximum Transmission Unit (MTU) network setting, and this Stack Overflow answer led me to the solution.

To determine the Ethernet interface in use for Internet connections, check the welcome message when the docker VM starts:

multipass shell docker
Welcome to Ubuntu 21.10 (GNU/Linux 5.13.0-46-generic aarch64)

  IPv4 address for docker0: xxx
  IPv4 address for enp0s1:  yyy
  IPv6 address for enp0s1:  zzz

For me, enp0s1 is the route to the Internet, so the fix is:

sudo ip link set dev enp0s1 mtu 1400

I encountered this when my firewall was still turned on, so I do not know the root cause...