arm64 vs x64: How to Check the Architecture of Any Machine, Binary or Container Image

Quick answer
One command tells you whether a machine is arm64 or x64 — but the same question applies to binaries, container images and Kubernetes nodes, and each has its own answer. Here's the full toolkit: uname, file, docker manifest inspect, node labels, and how to decode every alias from aarch64 to AMD64.
- Quick answer: check the machine
- The naming mess, decoded once
- Check a binary's architecture
- Check a container image's architecture
- exec format error — what it means and why you got it
12 min read · DevOps & Platform
On Linux and macOS, uname -m answers the question in one command — x86_64 means x64, aarch64 or arm64 means ARM. That covers the machine you are typing on. The trouble is that "is this arm64 or x64?" is really four different questions — about the machine, the OS, a binary, or a container image — and answering the wrong one is exactly how you end up staring at exec format error at 11pm.
This matters more now than it ever has. Your laptop is probably Apple Silicon (arm64), your CI runners are probably x64, and your cloud bill is quietly pushing you toward Graviton (arm64 again). Three environments, two architectures, and a build artifact that only works where it was compiled. Let's get the checks down cold.
Quick answer: check the machine
Linux
uname -mThat's it. Decode the output:
| Output | Architecture | Also known as |
|---|---|---|
x86_64 | 64-bit Intel/AMD | x64, amd64, AMD64 |
aarch64 | 64-bit ARM | arm64, ARM64 |
arm64 | 64-bit ARM (macOS reports this) | aarch64, ARM64 |
armv7l | 32-bit ARM (older Raspberry Pi) | armhf |
i686 / i386 | 32-bit Intel | x86 |
Want more detail — the exact CPU model, core count, whether it's a Graviton3 or an old Xeon:
lscpu
# Architecture: aarch64
# Model name: Neoverse-V1 ← that's Graviton3Or arch, which is an alias for uname -m on most systems. Or cat /proc/cpuinfo when you're inside a container so minimal it doesn't even ship uname.
macOS
Same command:
uname -m
# arm64 → Apple Silicon (M1/M2/M3/M4)
# x86_64 → Intel MacBut macOS has a trap the other platforms don't: Rosetta 2. An x64 process running under Rosetta on an Apple Silicon Mac sees x86_64 from uname -m — the translation layer is lying to it, by design. If your terminal (or the shell your CI agent spawned) is running translated, every architecture check downstream inherits the lie.
Check whether the current process is translated:
sysctl -n sysctl.proc_translated
# 1 → running under Rosetta (x64 process on ARM hardware)
# 0 → running natively
# error "unknown oid" → Intel Mac, no Rosetta involvedAnd to see the real hardware regardless of what your process thinks:
sysctl -n machdep.cpu.brand_string
# Apple M3 ProIf you develop with Apple's native container runtime, this hardware question is settled for you — Apple's container tool requires Apple Silicon outright, and its local Kubernetes plugin inherits that constraint.
Windows
echo %PROCESSOR_ARCHITECTURE%
:: AMD64 → x64
:: ARM64 → ARMIn PowerShell:
$env:PROCESSOR_ARCHITECTURE
# or, unambiguous about OS vs process:
[System.Runtime.InteropServices.RuntimeInformation]::OSArchitectureWindows has the same emulation caveat as macOS: an x64 process emulated on a Windows-on-ARM machine sees AMD64 in PROCESSOR_ARCHITECTURE. Don't reach for PROCESSOR_ARCHITEW6432 here — that variable only exists inside 32-bit (WOW64) processes, and an emulated x64 process doesn't get it at all. Ask the OS instead: systeminfo | findstr /C:"System Type" reports the real machine (ARM64-based PC). The no-terminal answer: Settings → System → About → System type, which reads something like "ARM-based processor".
The naming mess, decoded once
Every vendor picked a different name for the same two architectures, and the aliases are the single biggest source of confusion in this topic. Pin this table to a wall:
| These are all the same thing | Canonical name | Who uses which |
|---|---|---|
x64, x86_64, amd64, AMD64 | 64-bit x86 | x86_64: uname, rpm · amd64: Docker, Go, Debian · AMD64: Windows · x64: Microsoft docs, Node.js |
arm64, aarch64, ARM64 | 64-bit ARM (ARMv8+) | arm64: Docker, Go, macOS, Debian · aarch64: Linux uname, rpm · ARM64: Windows |
Two rules of thumb: Docker and Go always say amd64/arm64, and Linux's uname always says x86_64/aarch64. When a download page offers both aarch64 and arm64 tarballs, they are almost certainly the identical architecture packaged by two different naming conventions — read the fine print, but don't panic.
(Historical footnote on why "amd64": AMD designed the 64-bit extension of x86, Intel adopted it later. The name stuck in Debian and Docker even though most amd64 machines run Intel chips.)
Check a binary's architecture
The machine being arm64 doesn't mean the binary you just downloaded is. file reads the executable header on any platform:
file /usr/local/bin/terraform
# ELF 64-bit LSB executable, x86-64 ... → x64 Linux binary
# ELF 64-bit LSB executable, ARM aarch64 ... → arm64 Linux binary
# Mach-O 64-bit executable arm64 → native Apple Silicon binarymacOS adds universal binaries — one file containing both architectures. file shows both slices, but lipo is the purpose-built tool:
1lipo -archs /usr/bin/python3
2# x86_64 arm64e ← universal: runs natively on both
3# (arm64e is Apple's hardened arm64 variant — system binaries use it;
4# third-party universal binaries usually say plain arm64)
5
6lipo -archs ./some-old-tool
7# x86_64 ← will run on Apple Silicon, but only under RosettaThis is worth checking on any Mac dev machine that's been migrated from Intel via Time Machine — it's common to find half your Homebrew toolchain still running x64 under Rosetta, silently slower, years after the hardware changed.
On Windows, dumpbin /headers app.exe | findstr machine (Visual Studio tools) or simply file from Git Bash does the same job for PE executables.
Check a container image's architecture
This is the check that actually saves incidents. A container image is built for an architecture, and pulling the wrong one is the canonical way to break a deployment.
An image you already have locally:
docker inspect --format '{{.Os}}/{{.Architecture}}' nginx:latest
# linux/arm64Careful with what this tells you: it reports the architecture of the variant you pulled, which Docker selected to match your machine. On your M-series Mac it says arm64; the same tag pulled on your CI runner would say amd64. It does not tell you what the tag offers.
What a tag offers, without pulling anything:
docker manifest inspect nginx:latest | grep -A3 platformFor a multi-arch image you'll see a manifest list — one entry per platform (amd64, arm64, arm/v7, ...). For a single-arch image there's no list at all, just one manifest. That difference is the entire question of "will this image run on my Graviton nodes?".
The registry-native tools do it more cleanly and work in scripts:
# crane (from go-containerregistry) — ideal for CI checks
crane manifest nginx:latest | jq '.manifests[].platform'
# skopeo — same idea, daemonless
skopeo inspect --raw docker://nginx:latest | jq '.manifests[].platform'If you've moved off the Docker daemon entirely — see Docker vs Podman — podman manifest inspect and skopeo cover the same ground with no daemon in the loop.
Terraform Day-2 Operations Checklist
State hygiene, drift, imports, policy checks, and upgrade routines — everything after `terraform apply` works. Plain Markdown, commit it to your repo.
Free. Instant download. You'll also get the occasional deep-dive from the newsletter — unsubscribe anytime.
exec format error — what it means and why you got it
exec /app/server: exec format error
This error has exactly one meaning: the kernel was handed a binary for a different architecture. It is not a permissions problem, not a corrupt file, not a missing shared library — those all fail differently. The ELF header says aarch64, the CPU speaks x86_64 (or vice versa), and the kernel gives up at the first instruction.
The three ways it happens, in descending order of frequency:
- Built on an Apple Silicon laptop, deployed to x64.
docker buildwith no--platformflag builds for the host — arm64 — and the image sails through push and pull before dying at container start on the x64 node. - Single-arch base image on a mixed cluster. Your Dockerfile starts
FROM some-internal-base:1.4, which was only ever built for amd64, and the pod lands on a Graviton node. - A binary COPY'd into the image from the wrong release tarball. The image architecture is right; the vendored binary inside it isn't. This one is nasty because
docker inspectreports the image as fine — onlyfileon the binary inside reveals it.
In Kubernetes this surfaces as CrashLoopBackOff with the exec error in the logs, and it is one of the first things worth ruling out when a pod crashes instantly on some nodes and runs fine on others — the Kubernetes debugging guide covers the broader triage flow.
Why this got urgent: the three-way mismatch
For fifteen years everything was x64 and nobody ran these checks. Then three shifts landed at once:
- Dev laptops went ARM. Apple Silicon made arm64 the default architecture of the machine your Dockerfile gets written on.
- CI mostly stayed x64. GitHub Actions' standard
ubuntu-latestrunners are amd64 (ARM runners exist but are opt-in), so the artifact your pipeline builds usually doesn't match the laptop that authored it. - Cloud economics favour ARM. Graviton instances deliver roughly 20–40% better price-performance, which is why finance keeps asking about them — the Graviton migration guide covers that move end to end.
Laptop arm64, CI amd64, production either-or-both. Every artifact now crosses at least one architecture boundary, and the checks in this post are how you verify each crossing instead of discovering it at deploy time.
Building images that work on both
The fix for all of the above is publishing multi-arch images — one tag, a manifest list, and the runtime picks the right variant:
# one-time builder setup
docker buildx create --use
# build and push both variants under one tag
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/my-app:1.2.0 --push .The catch is how the foreign architecture gets built. On an x64 runner, the arm64 half builds under QEMU emulation — correct output, but anywhere from 3× to 20× slower, and occasionally flaky for JIT-heavy builds (JVM, Node native modules). The grown-up setup is native runners for each architecture — an amd64 runner and an arm64 runner (Graviton, or GitHub's ARM runners) each building their own half, stitched together with docker buildx imagetools create. Slower to set up, dramatically faster per build. Multi-arch also multiplies image storage, which makes image size optimization pay off twice.
The Kubernetes angle
Every node advertises its architecture as a label:
kubectl get nodes -L kubernetes.io/arch
# NAME STATUS ARCH
# node-1 Ready amd64
# node-2 Ready arm64On a mixed-architecture cluster — typically x64 nodes plus a Graviton node group — any workload whose image is single-arch must be pinned with nodeAffinity (or a simple nodeSelector):
1affinity:
2 nodeAffinity:
3 requiredDuringSchedulingIgnoredDuringExecution:
4 nodeSelectorTerms:
5 - matchExpressions:
6 - key: kubernetes.io/arch
7 operator: In
8 values: ["amd64"]The scheduler does not check image architecture — it will happily place an amd64-only pod on an arm64 node and let kubelet discover the problem as a crash. Mixed clusters are absolutely worth running for the Graviton savings, but the operating rule is: multi-arch images run unpinned, single-arch images run pinned, and nothing runs unverified. docker manifest inspect on every image in the workload is the pre-flight check.
Frequently Asked Questions
Are x64 and x86_64 the same thing?
Yes — and so are amd64 and AMD64. All four name the 64-bit x86 architecture. Likewise arm64, aarch64 and ARM64 all name 64-bit ARM. The vendor just determines the spelling: Docker says amd64/arm64, Linux's uname says x86_64/aarch64, Windows says AMD64/ARM64.
How do I check the architecture from inside a container?
uname -m works inside almost any Linux container — the answer comes straight from the kernel, and the tiny uname binary ships in busybox, alpine and every mainstream base image. On distroless or scratch images where you can't exec a shell at all, inspect the image from outside instead: docker inspect --format '{{.Architecture}}' <image>.
Can an arm64 machine run x64 containers?
Sometimes, slowly. Docker Desktop on Apple Silicon runs --platform linux/amd64 containers under QEMU/Rosetta emulation — fine for a quick test, wrong for benchmarks, and prone to breaking JIT-heavy runtimes. Native Linux hosts need binfmt/QEMU configured explicitly. Treat emulation as a convenience, never a deployment strategy.
Why does uname -m say x86_64 on my M-series Mac?
Your shell is running under Rosetta 2 — usually because the terminal app, or a parent process, is an x64 binary. Confirm with sysctl -n sysctl.proc_translated (returns 1 when translated) and check the real hardware with sysctl -n machdep.cpu.brand_string.
Is arm64 the same as ARMv8?
Effectively, yes. arm64/aarch64 is the 64-bit execution state introduced in ARMv8-A; everything you'll meet in cloud and on Apple Silicon (Graviton, Ampere, M-series) is ARMv8 or newer. 32-bit ARM (armv7l, armhf) is a different target and mostly survives on older Raspberry Pi hardware.
How do I know if a public image supports arm64?
docker manifest inspect <image> | grep architecture — no pull required. If the output lists both amd64 and arm64 platform entries, it's multi-arch. Docker Hub also shows supported architectures on each tag's page. Most official images have shipped arm64 for years; internal images are where the gaps live.
See also
- AWS Graviton arm64 migration guide — the reason most teams suddenly care about this
- Apple's container on macOS — the arm64-only runtime on Apple Silicon
- Docker vs Podman — daemonless tooling, including skopeo-style inspection
- Kubernetes debugging guide — where exec format error fits in pod triage
- Docker image optimization — keeping multi-arch images from doubling your registry bill
Official References
- Dockerfile best practices — layer caching, image size and build ordering
- Dockerfile reference — every instruction and its semantics
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


