October 30, 2022
Emulate the x86_64 architecture for development environments on an Apple M1
First, let’s clarify some terminology. The terms x86, x86_64, x64, and amd64 often refer to the same common Intel-based architecture found in most PCs and servers. In the same vein, the architecture used in Apple M1 processors may be referred to as ARM, ARM64, ARMv8 64, or aarch64. It can be confusing, but these terms are often used interchangeably.
The most convenient way to run foreign development environments on your M1 is using Docker or Lima, depending on your needs. Both leverage QEMU to handle CPU emulation, with varying performance penalties.
Docker
If you are running Docker Desktop, QEMU is already set up. If you use another platform as your Docker provider, you might need to run:
docker run --privileged --rm tonistiigi/binfmt --install allFor a quick approach, you can use a disposable container. For instance, the following command spawns a Python x86_64 workspace, mounts the current folder as a volume, and opens a shell.
docker run -it --rm --platform="linux/amd64" -v ${PWD}:/app -w /app python:3.10 bashYou can reuse a container, but they are ephemeral by nature. If you choose this route, remove --rm and add the --name flag to the command above.
You can check the available architecture options 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.10Pro tip: If you execute docker run without specifying a platform, the created container uses the last image pulled, regardless of its architecture. This behavior is confirmed on Docker 20.10. Example:
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_64If your local software project uses a Dockerfile, you can add the --platform flag to the FROM clause.
FROM python:3.10Lima
For developers accustomed to Linux, Lima is 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 perform generic Virtual Machine management. We will focus on the latter.
This approach has a more significant performance impact than Docker, but it offers a good balance of compatibility and ease of use for projects that do not run well within containers.
We will use Ubuntu as our base OS.
limactl start ubuntu-ltsWhen prompted, select “Open an editor to review or modify the current configuration”. The arch field determines the VM architecture; set it to x86_64. Also, modify mounts to fit your needs. You will likely add a relevant folder from your host machine (avoid using your entire home folder). Add writable: true so the guest machine can make changes. Finally, you might want to change the containerd field to false (since we will not need it).
To better understand the customization options, read the comments in this file.
After your VM starts, 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 -mNow, you have a traditional Linux environment ready with volume mounting and port forwarding. To check if both are running smoothly, go to a mounted folder and run:
python3 -m http.server 9000On your host machine’s browser, access http://localhost:9000 to see a simple web server displaying your files.