Docker on Apple Silicon: What’s Wrong and How to Cope

UPDATE: I’ve been keeping tabs on Rosetta-on-Linux in recent late-August macOS Sonoma betas and the latest have been handling every Intel container I’ve thrown at them. I don’t recommend running the beta for your day-to-day work yet—but take heart. Help is on the way!

If you, like me, have joined the ranks of MacBooks with Apple Silicon processors lately and use Docker, you may have been experiencing some pain.

These machines really are a giant leap over the Intel MacBook Pro I used to have. But the Docker landscape often requires dealing with the Intel platform, and the situation isn’t great for that — yet.

What’s Wrong

It’s important to know what the situation is before we can dive into how to cope with it. While Apple Silicon Macs can indeed run Intel macOS software with Rosetta, they can’t run Intel virtual machines. Docker is an interesting case. On Linux, Docker is just an application creating containers inside the existing operating system. On Macs, Docker uses a Linux virtual machine. On Intel Macs, that’s an Intel Linux virtual machine. On Apple Silicon, that’s an ARM Linux virtual machine.

But Docker on ARM can run Intel Docker images, because its Linux virtual machine has QEMU. When an Intel Linux binary runs, QEMU is called, and the Intel architecture is literally emulated. It works, but it’s slow, and my software team has definitely experienced some images that just don’t work at all.

Very recently, Apple added Rosetta support to Linux virtual machines. This still doesn’t mean you can have an Intel Linux virtual machine, but it does mean that you can use Rosetta instead of QEMU.

Current versions of Docker Desktop have a checkbox to turn this on, under “Features in development.” Unfortunately, there’s still some software it can’t run. GCC, for example, which is used to compile all sorts of Linux software, will sometimes crash Rosetta. When you’re doing Docker image development, building happens a lot. So, unfortunately, we can’t quite use Rosetta either just yet.

What You Can Do Locally

If you’re developing locally, your best bet is to seek out base images already built for the arm64 machine type. You can look for these on Docker Hub — look under the tags you want to use and check what builds are available.

These ARM-native builds will not only usually be the most reliable, but they’ll also take full advantage of the speed of your Apple Silicon.

If there aren’t builds yet, then you can try making them if you can find the Dockerfile source. Sometimes (since amd64-only images are generally older images), this can involve some Git history spelunking. I’ve used this with some success to get projects with older dependencies off the ground.

This is great for development but doesn’t help when you need to build Intel images to deploy. You could, of course, change your infrastructure to support ARM images. But if you can’t just yet, there are still options.

What You Can Do for Deployment

Your best option to get deployments off the ground is to get your Intel-based continuous integration system to build images for you. It’s hands-off and it works for every developer on your team with no extra setup.

But if you need to build images locally—say, to test a new build—or you just don’t have CI, you’re still not completely out of options.

I’ve deployed an Intel EC2 instance in the cloud that I can use with Docker remotely. It’s fairly simple to set up:

  1. Deploy an Ubuntu instance. I use a t2.medium instance; t2.micro would get overwhelmed pretty quickly.
  2. Make sure you can use SSH to log in. You may need to add a rule to your security groups to add your local IP address or use Tailscale. (Check out my two previous posts on Tailscale here and here.)
  3. Follow Docker’s directions to install Docker on Ubuntu, including the after-install instructions to add the “ubuntu” user to the “docker” group.

Now you’ve got a build host ready to use. You can use it by adding the -H flag to your Docker command line, or set the environment variable “DOCKER_HOST”. In both cases, use something like the following: ssh://[email protected].

Fair warning: you can also run Docker images on this host — it’s really convenient! But you can’t bind mount local code there, nor forward ports back to your Mac. Instead, you can do your development directly on the host, if you need these things. Visual Studio Code has some great features for remote development over SSH that will work with this setup.

Next up

The macOS 13.3 beta cycle is in progress, and as of this writing, that one key Rosetta issue is still a problem. I have hopes that it gets fixed, though it may be macOS 14 before it does.

Until then, I hope the advice in this post has helped you make your development environment a lot more pleasant to work in.

 
Conversation
  • Solomon H says:

    Great article, I’m not a Mac user but I may be teaching a course on Docker soon and wanted to understand the state of Docker on ARM Mac. Are the virtual machines running Docker type1 or type2 hypervisor? Does Apple allow any type1 hypervisor on it’s platform(s)?

  • Join the conversation

    Your email address will not be published. Required fields are marked *