Docker path · What a container is · Topic 3 of 12

Docker on a Mac is a Linux VM

Not started

Containers need a Linux kernel, and macOS does not have one. Docker Desktop solves this by running a small Linux virtual machine in the background, with the Docker Engine inside it. The docker command you type on the Mac is only a client: it sends API calls over a socket to the engine in the VM, and every image, container and volume lives on the VM's virtual disk.

Why it matters: Slow bind mounts, 'localhost' that is not your Mac, images that fail with 'exec format error': all of them follow from the VM that Docker Desktop runs for you.

  1. 1A Linux kernel is required

    macOS cannot run Linux containers itself, so Docker Desktop starts a small Linux VM and runs the Docker Engine inside it.

  2. 2The CLI is a client

    The docker command on your Mac talks to the engine in the VM over a socket; images, containers and volumes live in the VM's disk.

  3. 3Files and ports cross a boundary

    Bind mounts are shared into the VM, and published ports are forwarded back to the Mac, both at some cost.

  4. 4The CPU architecture matters

    On Apple silicon the VM is arm64; amd64 images need an emulator, or a multi-platform image with an arm64 variant.

1

How it works

The explanation, with examples you can run.

Seeing the VM

docker context ls# NAME              DOCKER ENDPOINT# desktop-linux *   unix:///Users/you/.docker/run/docker.sock docker info --format '{{.OperatingSystem}} {{.KernelVersion}} {{.Architecture}}'# Docker Desktop 6.12.54-linuxkit aarch64 docker run --rm alpine:3.22 uname -m# aarch64

The kernel is the VM's (a LinuxKit build), not Darwin. The VM is managed by a virtual machine manager: on Apple silicon the default is Apple's Virtualization framework, and recent releases also offer Docker VMM, Docker's own container-optimised VMM (still beta). Settings such as CPUs, memory and disk size apply to this VM, so a container can never use more memory than the VM was given.

Files: bind mounts cross a boundary

When you bind-mount a folder from your Mac (-v "$PWD":/app), Docker Desktop shares it into the VM, by default with VirtioFS. That works well for source code, but workloads that touch thousands of small files (installing node_modules, compiling, database files) are noticeably slower than on native Linux. Common fixes:

  • put dependency folders and databases in named volumes, which live inside the VM's disk;
  • use Compose develop.watch to sync changed files instead of mounting the whole tree.

Networking: three different localhosts

The VM has its own network stack. Docker Desktop forwards each published port (-p 8765:3000) to the same port on the Mac, so curl localhost:8765 works as on Linux. In the other direction, a container that needs a service on the Mac cannot use localhost (that is the container) and should use the special name host.docker.internal:

docker run --rm alpine:3.22 getent hosts host.docker.internal# 192.168.65.254   host.docker.internal

Features that assume the container and host share one network, such as --network host, behave differently than on a Linux server, so test anything network-sensitive on Linux before shipping.

CPU architecture

On Apple silicon the VM is arm64, so it runs linux/arm64 images natively. An image that only exists for linux/amd64 needs emulation (Rosetta or QEMU, if enabled in settings). Without it, it fails:

docker run --rm --platform linux/amd64 alpine:3.22 uname -m# exec /bin/uname: exec format error    (on a Desktop without amd64 emulation)

Even with emulation, amd64 images are slower and occasionally behave differently. The fix is to build and pull multi-platform images (see BuildKit), so every machine, from your laptop to an amd64 server, gets a native variant.

What carries over

Everything you learn here about images, layers, networks and volumes is the same on a Linux server; only the VM boundary is extra. When something works on your Mac but not in production (or the reverse), check the three usual suspects first: file sharing, networking through the VM, and CPU architecture.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.On a Mac, what does uname -r print inside a container?
    docker run --rm alpine:3.22 uname -r
  2. 2.Your API runs on the Mac at port 5432 and a container must reach it. Which address works?
  3. 3.npm install in a bind-mounted project folder is far slower than on Linux. Why?

0 of 3 right so far. Get all three to finish this topic.