How to cross compile using Docker

To cross compile a binary for a different OS and CPU architecture, using Docker, follow these simple steps:

1) Finding the right docker image

Since each docker image is built against a specific CPU architecture, the first step is to find a docker image that combines both the OS and CPU architecture you want to cross compile for.

You can see the CPU architecture an image was built for on Docker Hub by scanning the "OS/ARCH" column:

The most popular docker images tend to offer multiple builds, each targeting a different CPU architecture. The following shows the latest tag of ubuntu. As you can see, it offers builds for multiple CPU architectures:

Let's assume we want to build an ImageMagick binary for Ubuntu (ARM64) on our MacBook (x86, aka AMD64). Since the above shows  ubuntu  supports linux/arm64/v8, we're ready to move on to step 2!

2) Running the docker image

By default docker run will pick the image that matches your host machine's CPU architecture. This is for performance reasons, since the emulation adds overhead.

We can see docker picking the x86 image by default:

$ docker run ubuntu uname -p
x86_64

To force docker into selecting (and emulating the hardware for) the ARM64 version of the ubuntu image, we simply add the --platform flag:

$ docker run --platform linux/arm64/v8 ubuntu uname -p
aarch64

Excellent! We can now boot-up an Ubuntu ARM64 machine to build our native binaries on!

3) Building your native binaries

The rest is easy: just compile your binaries inside the docker container we launched above. Remember: the --platform flag is crucial! (You can always run uname -p inside your container to make sure you are really being emulated.)

Be warned: CPU architecture emulation adds significant overhead. In our experience, compile times can take up to 5x longer when emulating via the --platform tag. Maybe go grab a coffee after starting make!