I work with Linux in Docker on the daily. And the people and machines I support have a blend of Intel/AMD and ARM/Graviton/AppleSilicon chips.
And the thing that bites me regularly is that different operating systems return different values for uname -m
, even when they're the same thing.
- We have users on macOS, Windows, and Linux.
- We have a blend of worker laptops using both Intel/AMD and Apple Silicon CPU architectures.
- We have cloud servers in AWS, GCP, and Azure.
- We have a blend of cloud servers using both Intel/AMD and Apple Silicon CPU architectures.
- We rely heavily on Docker/Terraform for consistency/repeatability, and to better scale the perpetually-limited resources of our DevOps/SRE/Cloud/Platform engineering teams.
- Docker runs natively on Linux.
- Docker runs virtualized in macOS and Windows.
- Software running inside the Linux-based Docker containers is most efficient when compiled for the current CPU architecture.
- Out on the internet, people build packages that can be installed. Many are not inside the Linux system’s package manager, and must be installed from the web. The people who publish these packages use a variety of identifiers for Intel-compatible vs ARM-compatible CPU architectures. There is no consistency.
When building tooling/solutions for a heterogenous set of machines across an enterprise, you need to solve for (at least) the following matrix.
- Current OS
- Current CPU architecture
- Package filenames on the internet
Deploying software as Docker containers (running Linux) helps normalize things like:
- Relying on GNU vs BSD-flavored CLI tools
- Download packages into the Docker container, worrying only about Linux
- Deploying software across worker laptops running different host operating systems
- Deploying software to Linux servers in the cloud
But these solutions don't solve the (relatively new) problem of an uptick of 64-bit ARM software/CPUs being added to the matrix — and the fact that these are not referred-to in a unified, consistent way.
OS | 64-bit Intel-compat | 64-bit ARM |
---|---|---|
macOS | x86_64 |
arm64 |
Red Hat family¹ | x86_64 |
aarch64 |
Debian family² | amd64 |
arm64 |
Busybox family³ | x86_64 |
aarch64 |
Windows WSL2⁴ | Varies | Varies |
- ¹ Red Hat family includes Red Hat Enterprise Linux, CentOS, Fedora, Amazon Linux, and others.
- ² Debian family includes Debian, Ubuntu, Linux Mint, and others.
- ³ Busybox family includes Busybox, Alpine Linux, and others.
- ⁴ Windows WSL2 returns whatever the underlying Linux installation says.
There are different names for (essentially) the same CPU architectures. Different vendors use different names for the same thing.
Here's an (extremely) brief overview of modern CPU architectures that you most commonly find in cloud service providers and modern desktops/laptops.
This is meant to be illustrative, not comprehensive. As of today, these are the top 2 by a large margin.
Family | Arch IDs | Description |
---|---|---|
x86 |
x86_64 , amd64 , x64 |
Intel’s 80x86 line of CPUs, and AMD clones. Shortened to x86 (or sometimes x64 ), these are the newer 64-bit models. Includes Amazon EC2 instances powered by Intel Xeon™ or AMD EPYC™ CPUs, and Intel i-Series Macs. |
arm |
arm64 , arm64v8 , arm64v9 , aarch64 |
ARM v8/v9, 64-bit. AWS Graviton, Apple A7 and newer (including M-series). All 64-bit ARM chips are ARM v8/v9, but the inverse is not true. arm64 == ( arm64v8 || arm64v9 ) . Includes Amazon EC2 instances powered by AWS Graviton™ CPUs and Apple M-Series Macs. |
arch.py $(uname -m) --x86 x86_64 --arm aarch64
arch.py $(uname -m) --x86 amd64 --arm arm64
arch.py $(uname -m) --x86 x86_64 --arm arm64
- Docker Compose uses
x86_64
andaarch64
in the filenames of their releases. - Docker Compose is written in Go, which compiles (easily) for all Linuxes (e.g., RHEL, Debian, Alpine).
- This will determine the correct URL to construct based on the value of
uname -m
for the present system, at runtime.
wget -q "https://github.com/docker/compose/releases/download/v${COMPOSE_VERSION}/docker-compose-linux-$(./arch.py $(uname -m) --x86 x86_64 --arm aarch64)" \
-O /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
I use these exact lines in my Dockerfiles that I write for multi-architecture Docker image builds. This script is extremely lo-fi, but it is composable and has saved me dozens of hours over the last couple of years.