TIL: Docker on Apple Silicon - Native ARM or x86 Emulation?

#docker#apple-silicon#m1#arm64#amd64#platform#debugging#TIL

Hit an interesting issue today while building a Docker image for a ship propulsion system (yes, really). The build kept failing with a cryptic error about package architecture mismatches. Turns out I was accidentally trying to install x86_64 packages on an ARM64 system.

But wait - I’m on a MacBook Pro M1. Docker runs fine. I’ve never had to think about architecture before. So what’s actually happening under the hood?

The Error

dpkg: error processing archive /tmp/kvaser.deb (--unpack):
  package architecture (amd64) does not match system (arm64)

The Dockerfile was trying to install a vendor-provided .deb package that only comes in x86_64 (amd64) flavor. My colleague handed me an amd64 package for an ARM64 production target. Great.

So… Native or Emulated?

By default, Docker on Apple Silicon runs containers natively in ARM64.

When you docker run ubuntu on an M1 Mac, you’re getting an ARM64 Ubuntu image, and everything runs at native speed. No emulation involved.

But here’s the magic: Docker Desktop on Apple Silicon includes Rosetta 2 integration and QEMU emulation, so you can run x86_64 containers if you explicitly ask for them.

How to Run x86_64 Containers on ARM

You have two options:

Option 1: Build Command Flag

docker build --platform linux/amd64 -t your-image .

Option 2: Dockerfile Declaration

FROM --platform=linux/amd64 ubuntu:24.04

Option 3: Docker Compose

services:
  your-service:
    platform: linux/amd64
    build:
      context: .

When you specify --platform linux/amd64, Docker will:

  • Pull x86_64 base images
  • Run the build process under emulation (using QEMU)
  • Produce an x86_64 container image

Performance note: Emulated x86_64 builds are noticeably slower than native ARM64 builds. For CI/CD pipelines, you’ll want to use native ARM64 runners or x86_64 build machines.

The Gotcha: Cached Images

If you switch platforms, Docker might try to reuse cached layers from the wrong architecture:

image with reference your-image was found but does not match
the specified platform: wanted linux/amd64, actual: linux/arm64

Fix it by forcing a clean rebuild:

docker-compose build --no-cache
# or
docker rmi your-image && docker-compose build

When Do You Need x86_64?

Most of the time, you don’t. Modern base images (Ubuntu, Alpine, Python, Node, etc.) are multi-arch and Docker automatically pulls the right one.

You do need to explicitly specify x86_64 when:

  • Using vendor-provided binaries that only ship as amd64 (like my Kvaser SDK situation)
  • Your production environment is x86_64 and you need exact platform parity
  • Working with legacy containers that don’t have ARM64 builds

Production Mismatch

In my case, the production system (embedded ARM64) needed ARM64 binaries, but we only had x86_64 packages from the vendor. The solution? Not a Docker problem - needed to get the vendor to provide ARM64 packages or find an alternative CAN driver.

Building x86_64 images on my M1 for an ARM64 production target will just delay the inevitable failure.

Checking What You’re Running

# Check your Mac's architecture
uname -m
# arm64

# Check a running container's architecture
docker run --rm alpine uname -m
# aarch64 (ARM64) by default
# x86_64 if you specified --platform linux/amd64

# Inspect an image's platform
docker image inspect ubuntu:24.04 | grep Architecture

TL;DR: Docker on Apple Silicon runs ARM64 natively by default, but can emulate x86_64 when you explicitly specify --platform linux/amd64. Most images are multi-arch so you don’t need to think about it, but vendor-provided binaries and platform-specific packages will bite you. When in doubt, check what architecture your production system actually needs.