July 7, 2026 · 6 min read
Apple container vs Docker Desktop: What the Source Says
A source-backed comparison of Apple container and Docker Desktop on macOS, using GitHits to trace every architecture claim to docs and code.
Apple shipped container this year, an open-source tool for running Linux containers on Apple silicon. It is written in Swift and built on Apple’s Containerization package, which drives a Virtualization.framework backed VMM on macOS. It runs the same OCI images you already build. So on the surface it looks like Docker Desktop with a new logo, and most of the coverage treated it that way.
It isn’t. The two tools draw the virtualization boundary in completely different places, and that one decision ripples out into isolation, memory behavior, and which workflows feel native. We wanted the difference grounded in the implementation, not the keynote, so we explored both projects with GitHits and traced every architectural claim back to source across four repositories.
Here is the whole thing in one sentence. Apple container boots a separate lightweight VM for each container, each with its own kernel. Docker Desktop runs a single Linux VM and packs the daemon and every container inside it, sharing one kernel. Everything else in this post follows from that.
| Question | Apple container | Docker Desktop on macOS |
|---|---|---|
| Image format | Consumes and produces OCI-compatible images | Runs OCI images through Docker Engine and containerd |
| macOS VM model | One lightweight VM per container | One Docker-managed Linux VM for the daemon and all containers |
| Kernel sharing | Each container has its own guest kernel in its own VM | Containers share the kernel inside the Docker Desktop VM |
| Control plane | container-apiserver runs as a host launch agent | The Docker daemon runs inside the Docker Desktop Linux VM |
| Runtime path | container-apiserver launches container-runtime-linux per container | dockerd drives containerd and the default runc v2 shim |
Research Prompt
Here is the prompt we handed a GitHits-enabled agent:
Compare apple/container with Docker Desktop on macOS. Use
GitHits to inspect apple/container, apple/containerization,
docker/docs, and moby/moby. Verify the isolation model, runtime
chain, OCI image compatibility, and performance/resource
tradeoffs from source or official docs. Cite exact repository
permalinks for every architectural claim, and call out which
claims come from Docker Desktop docs versus moby source.
Everything below links to the exact source it came from. That was the point of the exercise: no claim survives unless the code or the docs back it.
Evidence
Apple’s own docs/technical-overview.md says it plainly. The conventional macOS setup runs one Linux VM hosting many containers; container instead spins up a lightweight VM for each container you create. It keeps container-apiserver on the host and has it launch a per-container container-runtime-linux helper.
The code agrees with the prose, which is not always a given. APIServer+Start.swift wires up the container, network, kernel, volume, and disk-usage services. RuntimeLinuxHelper+Start.swift registers the launchd label com.apple.container.runtime.container-runtime-linux and starts a runtime service scoped to exactly one container.
Go one layer down into Containerization and the per-container claim stops being marketing. ContainerManager constructs a VZVirtualMachineManager, a Virtualization.framework backed VMM. LinuxContainer is the lifecycle type for a Linux container running inside a VM, and VZVirtualMachineInstance wraps a VZVirtualMachine with its own CPU, memory, kernel, root filesystem, mounts, and network interfaces. The kernel really is a property of each instance. It is not a figure of speech.
Docker Desktop needs two sources instead of one, and the reason is itself informative: the macOS VM boundary is a Docker Desktop feature, not part of the open engine. The docs put the Docker daemon and containers in a lightweight Linux VM, and the security FAQ describes containers running in a customized Linux VM. Inside that VM, moby source shows the engine path you would expect: the daemon builds a libcontainerd client, and the Unix runtime config sets the default runtime to runc via the io.containerd.runc.v2 shim, wired as plugins.RuntimeRuncV2. Two claims from Docker’s docs, one from moby’s source, and they line up.
What Changes
The isolation boundary is the whole ballgame.
Apple container wraps a VM around each individual workload. If a container gets popped, the blast radius is one guest kernel, and the next thing over is a different VM entirely. That is a genuinely nice property for the messier corners of modern development: coding-agent sandboxes running who-knows-what, generated build steps you did not write, security experiments you would rather not run next to your real work. When you need harder separation, you already have it. There is no shared kernel to escape into.
Docker Desktop draws the boundary around the environment as a whole. Every container shares the Desktop VM and its one guest kernel, with the macOS host sitting outside. For a dense Compose stack, a web app plus a database plus a queue plus a couple of sidecars, that shared VM is the sane default, and frankly it is what a decade of tooling already assumes. Cross-container networking just works because everything lives in the same place.
Resources push back in the other direction, and this is where per-container VMs stop being free. The overview is candid about a macOS memory-ballooning limitation: pages freed inside a guest are not always returned to the host. Run twenty little services this way and you feel it. One shared VM is simply easier on your machine when the container count climbs, which is the tradeoff you are accepting in exchange for a stronger boundary.
Practical Choice
The decision is less “which tool is better” and more “which boundary do you actually need.”
Reach for Apple container when the boundary is the point: agent sandboxes, tests for generated code, untrusted build scripts, or any Linux tool you would rather quarantine from the rest of your machine. The per-workload VM is doing real work for you there.
Stay on Docker Desktop when the workflow is the point: existing Dockerfiles and Compose files, onboarding a teammate this afternoon, matching CI, ordinary multi-service development. The ecosystem gravity is real and there is no prize for fighting it.
The two paths, side by side:
OCI image
├─ apple/container
│ CLI -> host apiserver -> per-container runtime
│ -> one VM per container
│
└─ Docker Desktop
docker CLI -> Docker-managed Linux VM -> dockerd
-> containerd -> runc
-> containers sharing one guest kernel
One caveat worth stating loudly. On Linux, Docker runs containers directly on the host kernel with no Desktop VM in the picture at all. This whole comparison is Docker Desktop on macOS against Apple container on macOS. Move to a Linux host and the terms change.
Summary
Same images, different boundary. Apple container gives every container its own VM and its own kernel; Docker Desktop shares one of each across the lot. That makes Apple container the better pick when per-workload isolation is what you are after, and Docker Desktop the better pick when ecosystem and workflow are. Apple did not ship a Docker Desktop clone with a nicer CLI, and Docker Desktop did not become obsolete the day Apple shipped a runtime. They optimize for different boundaries. Pick the one that matches the work in front of you.