While there are many things to like about Apple’s laptops, one of the more frustrating aspects is the number of applications and images that are not designed to support ARM processors. Over the last few years, we’ve seen ARM chips suddenly surge into mainstream laptops and servers. It makes sense – the chipsets offer high performance at a low cost.
Sadly, not all containers and applications have been updated to support the additional architecture. This means that we often have to accommodate these tools. Dev containers add an additional challenge. They limit what build arguments we can provide, preventing us from taking control of parts of the process (such as the architecture). For example, while I can include the current Azure CLI in my
arm64 images, the consistently fails to work because portions of the package target Intel/AMD chipsets. I have to rely on
Thankfully, it’s not too difficult to force the containers to be 64-bit. First, you need to make sure you have Rosetta 2 installed. This allows you to run code meant for Intel/AMD chips. A simple command line will configure that for us:
1softwareupdate --install-rosetta --agree-to-license
Next, we update the Dockerfile. The platform defaults to
linux/arm64 on macOS when building images. We need
linux/amd64. Normally, we would use the Docker command line to specify a platform when we need to override the default. Because we’re in a dev container, that’s not an option. Instead, we have to force the platform by adding a command to the
Dockerfile itself. We modify the initial
FROM statement to specify a platform option:
1FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/ruby:0-3.1
That’s all it takes to be running a different architecture! Now, if we pay careful attention to the build (or the logs), we see a warning:
1WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested.
This is just a warning to indicate that there is a mismatch between the image’s platform and the host environment. To solve this, we need to update the dev container’s
devcontainer.json to specify a runtime platform. In this case,
runArgs is available to allow us to configure the
1"runArgs": ["--platform=linux/amd64" ],
And now there’s no more warning. If you’re using this approach, be aware that some variable values that you may have used could become very misleading. For example,
TARGETARCH will remain
arm64, since that is the target platform Docker requested. This may impact some of your scripts. The values you’re likely to see:
There is nothing to indicate amd64. If your scripts rely on knowing the platform, you may need to rely on native Linux functionality to query those details when you’re forcing the architecture.
Why not do this all the time? The simple answer – it’s much slower. You’re using QEMU to emulate the other platform. As a result, code can take much longer to execute. For example, building Hugo is typically under a minute natively. Emulated, it may take 5 - 10 minutes to do the same task. You’ll have to weigh the tradeoffs and how much they impact your workflow. In my Hugo example, the impact was almost exclusively seen during the build of the container. If I pre-build the base image, I can reference an
image instead of a
build in my
devcontainer.json. The impact would then be minimal, since everything else runs quickly.