Running x86-64 Docker Images on ARM

Need to run Intel-based Docker images on a Linux ARM machine? Run this command:

docker run --rm --privileged aptman/qus -s -- -p x86_64

That's it! Try running your Docker image. Keep reading to learn how it works and what you need to be aware of.

How it works

When you run any executable, it gets sent to the kernel so that it can check its architecture. You might have seen this error message before:

exec user process caused "exec format error"

This means the architecture didn't match up with what's supported for your system. The Linux kernel has a feature called binfmt_misc which allows us to define our own handlers for arbitrary executable formats. We use this feature to load x86-64 executables using QEMU.

QEMU is a suite of machine emulators for a huge variety of system architectures. In this case, we don't actually emulate a system with a kernel but instead, we use QEMU's user space emulator. This means it has much less overhead than emulating a complete system. Privileged actions like writing to files or sending network requests are delegated directly to the Linux kernel.

The qus Docker image packages this whole setup so that all you need to do is run it as a container. It contains the required QEMU binaries and also automatically registers them into binfmt_misc. As it requires direct access to the Linux kernel, we use the --privileged Docker flag. You can learn more about it on its project page.

Caveats

Performance

As we're emulating (not virtualizing!) the x86-64 architecture, the performance penalty is going to be significant. That is, it could be up to an order of magnitude slower for some workloads. I've found that it works fine for a small web server.

Reboots

The Linux kernel resets any registered binfmt_misc handlers on system reboots. To persist these handlers across reboots, you can use crontab to run a script containing the Docker command at startup:

$ crontab -e
# ...
@reboot sh $HOME/script.sh

Further resources