snippets > how-emulate-x86-64-architecture-development-environments-apple-m1

October 30, 2022

How to emulate the x86_64 architecture for development environments on an Apple M1

First of all, let’s get some terminology out of the way. In the wild, the terms x86, x86_64, x64, and amd64 can be used to refer to the same very common Intel-based architecture we find on most PCs and servers. In the same vein, the architecture used on Apple M1 processors may be referred to as ARM, ARM64, ARMv8 64, and aarch64. It’s kind of a mess, but don’t think too much about it. Just remember the names.

The most convenient way of running foreign development environments on your M1 is using Docker or Lima, depending on your needs. Both leverage QEMU under the hood to handle the actual CPU emulation and do so with different degrees of performance penalties.

Docker

If you’re running Docker Desktop, you already have QEMU set up for you. If you’re using another platform as your Docker provider, you might need to run this:

docker run --privileged --rm tonistiigi/binfmt --install all

For a quick and dirty approach, you can use a disposable container for running your environment. For instance, the following command will spawn a Python x86_64 workspace, mount the current folder as a volume, and open a shell for you.

docker run -it --rm --platform="linux/amd64" -v ${PWD}:/app -w /app python:3.10 bash

It’s possible to reuse a container if you want to, but they’re ephemeral by nature, so don’t grow too attached to them. If you’re going down this route, remove --rm and add the --name flag to the command above to make your life easier later.

You can get to know the architecture options you have available for any container image by running:

# Get data (including supported architectures) about an image stored in a remote registry (e.g. Docker Hub)
docker manifest inspect python:3.10

# Get data on an image locally available on your machine
docker image inspect python:3.10

Pro tip: if you execute docker run without specifying a platform, the created container will use the last image pulled regardless of its architecture. This behavior is confirmed to work on Docker 20.10. See an example below:

docker pull python:3.10 
docker run python:3.10 uname -m
# aarch64

docker pull --platform="linux/amd64" python:3.10
docker run python:3.10 uname -m
# x86_64

If your local software project uses a Dockerfile to manage its image build process, you can add the --platform flag to the FROM clause.

FROM --platform="linux/amd64" python:3.10

Lima

For developers accustomed to working with Linux, Lima can prove itself a powerful addition to their toolbox. Its main use case is running containerd on MacOS, but it can also act as a Docker Desktop replacement or even do generic Virtual Machine management. We’re going to focus on this last one today.

This approach has a more significant performance impact than Docker, but it can be a good combination of compatibility and ease of use for projects that don’t run well within containers.

Let’s use Ubuntu as our base OS here.

limactl start ubuntu-lts

When prompted, select “Open an editor to review or modify the current configuration”. The arch field determines the architecture being used by our VM, so set it to x86_64. Also, feel free to modify mounts to fit your needs. You’ll likely add a relevant folder from your host machine there (try not to use your entire home folder for that). And don’t forget to add writable: true to it so the guest machine can make changes to your files. Finally, you might want to change the containerd field to false (since we won’t need it).

To better understand of all the customization options available on the file you’ve just edited, read the comments on this file.

After your VM has started, run:

# Open a shell in your new guest machine
limactl shell ubuntu-lts

# Confirm the architecture being run (you should see "x86_64" as the output)
uname -m

Now, you have a traditional Linux environment ready with volume mounting and port forwarding out of the box. To check if both are running smoothly, go to a mounted folder and run:

python3 -m http.server 9000

On the browser of your host machine, you should be able to access http://localhost:9000 and see a simple web server displaying your files.