- Published on
A Deep Dive into apple/container — The Lightweight VM Approach to macOS Containers
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction — Why It Is Hot Again
- Background — Why Containers Are Hard on macOS
- Docker Desktop Architecture — The Light and Shadow of One Big VM
- The apple/container Approach — Container Equals Lightweight VM
- WWDC26 — The Container Machine Feature
- Architecture Comparison — Four Tools
- Hands-On Usage
- Running amd64 Images with Rosetta
- OCI Compatibility — The Ecosystem Stays
- Performance and Security Trade-offs
- A Real-World Scenario — Building an AI Coding Agent Sandbox
- Operational Tips and Troubleshooting
- Checklist Before Migrating from Existing Tools
- Limitations and Critical Perspectives
- Outlook
- Closing
- References
Introduction — Why It Is Hot Again
For years, running containers on macOS has been synonymous with "boot one Linux VM and run Docker inside it." Docker Desktop, Lima, Colima, and OrbStack differ in implementation details, but the big picture was the same. Then Apple open-sourced apple/container at WWDC25 and flipped that assumption: a one-to-one model that attaches a dedicated lightweight VM to every single container.
In June 2026, the Container Machine feature announced at WWDC26 set GeekNews and Hacker News on fire again. With a general-purpose Linux machine mode that automatically mounts your home directory and storage, reactions poured in along the lines of "so even the Lima or WSL style Linux dev environment is now solved by a first-party Apple tool." In the GeekNews comment threads, enterprise users tired of Docker Desktop licensing costs cheered, while realists countered that "without the compose ecosystem, we cannot switch."
The timing also coincides with the explosion of demand for isolated execution environments as AI coding agents have gone mainstream. In an era where agents like Claude Code autonomously build and test code for hours at a time, the ability to rapidly create and destroy strongly isolated sandboxes per agent is no longer a convenience — it is a security requirement. All the more so after the June 2026 npm supply chain attack that reached as far as Red Hat Cloud Services.
In this post we will walk through the fundamental limits of Docker Desktop on macOS, the architecture of apple/container, Container Machine, hands-on usage, and a checklist to review before switching.
Background — Why Containers Are Hard on macOS
Let us make the premise explicit first. What we usually call a container (an OCI container) is a combination of Linux kernel features. Namespaces isolate the process, network, and filesystem views, cgroups limit resources, and OverlayFS merges image layers. Every one of these exists only in the Linux kernel.
The XNU kernel in macOS has none of these features. Therefore, to run Linux containers on macOS, a Linux kernel must exist somewhere — which means a virtual machine is required. The differences between all macOS container tools ultimately come down to "how, how many, and how lightly do you boot VMs."
Docker Desktop Architecture — The Light and Shadow of One Big VM
Docker Desktop boots a single Linux VM on macOS and runs dockerd (the Docker daemon) inside it. Every container runs as a sibling process inside this one shared VM.
+---------------------------------------------------------------+
| macOS Host |
| |
| docker CLI ----(API)----> +--------------------------------+ |
| | Linux VM (single, shared) | |
| Docker Desktop GUI | | |
| | dockerd + containerd | |
| | +-----------+ +-----------+ | |
| | | container | | container | | |
| | | A | | B | | |
| | +-----------+ +-----------+ | |
| | +-----------+ +-----------+ | |
| | | container | | container | | |
| | | C | | D | | |
| | +-----------+ +-----------+ | |
| +--------------------------------+ |
+---------------------------------------------------------------+
The advantages of this design are clear. The experience of using Docker on a Linux server carries over almost verbatim, and because there is only one VM, all containers share the image cache and the network. When you bundle multiple containers with compose, they live in the same VM, so communication is fast.
But the limitations also stem from the structure.
First, resource preemption. You must pre-allocate a large slice of CPU cores and memory to the single VM, and those resources stay claimed even when zero containers are running. A VM allocated 8GB contributes to macOS memory pressure even while idle.
Second, weakened isolation. All containers share the same kernel, so a single container escape vulnerability puts every other container in the same VM inside the blast radius. It is an uncomfortable structure for running untrusted code — say, code generated by an AI agent, or CI builds of external pull requests.
Third, file sharing performance. Bind-mounting a macOS directory into the VM has to go through a filesystem bridge such as VirtioFS or gRPC FUSE, which has long been a staple of Docker Desktop performance complaints.
Fourth, licensing cost. Docker Desktop requires a paid subscription for companies beyond a certain size. This is also the direct backdrop against which Colima and OrbStack grew.
The apple/container Approach — Container Equals Lightweight VM
apple/container changed the premise. Instead of making a container a process inside a shared VM, it boots a dedicated lightweight VM for every container.
+---------------------------------------------------------------+
| macOS Host |
| |
| container CLI (Swift) |
| | |
| | Virtualization.framework / Containerization package |
| v |
| +-------------+ +-------------+ +-------------+ |
| | micro VM 1 | | micro VM 2 | | micro VM 3 | |
| | | | | | | |
| | linux kernel| | linux kernel| | linux kernel| |
| | vminitd | | vminitd | | vminitd | |
| | container A | | container B | | container C | |
| +-------------+ +-------------+ +-------------+ |
| |
| number of VMs = number of running containers (1:1) |
+---------------------------------------------------------------+
Let us look at the key building blocks one by one.
Virtualization.framework
This is the high-level virtualization framework Apple has shipped since macOS Big Sur. Built on top of Hypervisor.framework, it provides virtio-based disk, network, and memory balloon devices. It is optimized for Apple Silicon, so VM creation and boot are very fast. Existing tools like Lima and UTM already used this framework as a backend, but apple/container is different in that it uses it directly for per-container isolation.
The Containerization Swift Package
The library underpinning apple/container, also open source. It provides Swift APIs for pulling and unpacking OCI images, assembling a minimal Linux root filesystem, booting the VM, and launching the container process. To shorten boot time it uses a minimized Linux kernel configuration, and for the init system it uses vminitd, written in Swift. vminitd is not a general-purpose init like systemd — it is a purpose-built init with only the minimum features needed to start one container process. As a result, container (that is, VM) startup drops to the sub-second range.
Daemonless Design
There is no always-on root daemon like Docker's dockerd. The container CLI spawns helper processes on demand, which shrinks the attack surface and consumes no resources while idle. The VM itself disappears together with the container when it exits, so the "VM hogging 8GB while unused" problem simply does not exist structurally.
WWDC26 — The Container Machine Feature
Where the apple/container of WWDC25 focused on running containers, the Container Machine added at WWDC26 expands the territory toward general-purpose Linux development environments. The key points that made waves on GeekNews are as follows.
First, automatic home directory mounting. When you create a machine, your macOS home directory is automatically mounted inside the Linux machine, so you can work with host files with zero extra configuration. Compared to hand-editing mount settings in Lima YAML files, the barrier to entry has dropped dramatically.
Second, automatic storage provisioning. A machine-dedicated disk image is created and managed automatically, so users no longer need to think about disk sizes or formats up front.
Third, WSL-like ergonomics. Dropping into a Linux shell with a single command and sharing files naturally with the host closely resembles the WSL experience on Windows. That is why so many comments declared that "the WSL moment for macOS has arrived."
With this, apple/container now has two modes: a container mode that lives briefly and is strongly isolated, and a machine mode that lives long and integrates tightly with the host. The natural picture is to use the former as a sandbox for AI coding agents and the latter as your daily development environment.
Architecture Comparison — Four Tools
Here is a one-page structural comparison of the four macOS container tools.
[Docker Desktop] [Lima / Colima]
macOS macOS
| |
+-- single shared VM +-- single shared VM (QEMU or VZ)
| |
+-- dockerd +-- containerd / dockerd
+-- N containers +-- N containers
[OrbStack] [apple/container]
macOS macOS
| |
+-- single shared VM +-- micro VM 1 -- container 1
| (custom optimized) +-- micro VM 2 -- container 2
+-- N containers +-- micro VM N -- container N
(no shared kernel, VM to container is 1:1)
The main characteristics in table form. Per the writing rules, special characters are avoided in cells and numbers are spelled out.
| Aspect | Docker Desktop | Lima and Colima | OrbStack | apple container |
|---|---|---|---|---|
| VM model | single shared VM | single shared VM | single shared VM | one VM per container |
| Kernel sharing | shared by all containers | shared by all containers | shared by all containers | none |
| Daemon | dockerd resident | containerd resident | own resident daemon | daemonless |
| Isolation level | process isolation | process isolation | process isolation | hardware virtualization |
| License | paid for enterprises | open source | free personal, paid business | open source Apache 2 |
| compose support | official | via docker compatibility | official | not supported |
| Implementation | mostly Go | mostly Go | closed source | Swift |
| Idle resource use | full VM allocation always | full VM allocation always | dynamically adjusted | near zero with no containers |
Hands-On Usage
Installation
You need an Apple Silicon Mac and macOS 15 or later; macOS 26 or later is recommended for full networking features. Download the signed installer package from GitHub releases or install via Homebrew.
# Install via Homebrew
brew install --cask container
# Start the API service (helper) — once
container system start
# Check status
container system status
Pulling Images and Running Containers
If you are a Docker user, the command scheme will feel familiar.
# Pull an image (plain OCI images from Docker Hub work as-is)
container image pull alpine:latest
# Run an interactive container — this one line boots a dedicated VM
container run -it alpine:latest sh
# Detached run with a published port
container run -d --name web -p 8080:80 nginx:latest
# List running containers
container list
# Logs and teardown
container logs web
container stop web
container delete web
The most striking part in practice is startup speed. Even though container run includes a kernel boot, a shell appears in under a second. This is the point where the intuition "it is a VM, so it must be slow" breaks down.
Building Images
Dockerfiles work as-is. Builds are performed inside a dedicated builder VM running BuildKit.
# Start the builder (once; resources configurable)
container builder start --cpus 4 --memory 8g
# Build from a Dockerfile
container build --tag myapp:dev --file Dockerfile .
# Multi-architecture build (arm64 + amd64)
container build --arch arm64 --arch amd64 --tag myapp:multi .
# Push to a registry
container registry login ghcr.io
container image push ghcr.io/myorg/myapp:dev
# An ordinary Dockerfile works unchanged
FROM golang:1.24-alpine AS build
WORKDIR /src
COPY . .
RUN go build -o /out/server ./cmd/server
FROM alpine:3.21
COPY /out/server /usr/local/bin/server
EXPOSE 8080
ENTRYPOINT ["server"]
Volumes and File Sharing
Host directories are mounted into the VM through virtio-based sharing.
# Mount the current directory as /work in the container
container run -it --volume "$PWD:/work" --workdir /work alpine:latest sh
# Create and use a named volume
container volume create pgdata
container run -d --name db --volume pgdata:/var/lib/postgresql/data postgres:17
Networking
On macOS 26 and later, each container receives its own dedicated IP address. Unlike the Docker Desktop model where containers carve up ports inside one VM, inter-container communication behaves much closer to a real network topology.
# Check container IPs
container list --format json
# Create a user-defined network (macOS 26 and later)
container network create devnet
container run -d --network devnet --name api myapp:dev
container run -d --network devnet --name cache redis:7
# inside devnet, containers can reach each other by name
Running amd64 Images with Rosetta
When you need to run amd64 (x86_64) only images on Apple Silicon, you use Rosetta 2 translation. Virtualization.framework offers the ability to execute x86_64 binaries inside a Linux VM through Rosetta, and apple/container leverages it directly. It feels dramatically faster than QEMU emulation.
# Run while explicitly requesting the amd64 image
container run --arch amd64 -it amazonlinux:2023 bash
# With a multi-arch manifest, arm64 is selected by default
container image pull --arch amd64 mysql:8.4
Rosetta translation does have limits, though. Some instruction set extensions such as AVX512 are not supported, and JIT-heavy workloads can show a wider performance gap versus native arm64. Building multi-arch images remains the proper long-term answer where possible.
OCI Compatibility — The Ecosystem Stays
The decisive reason apple/container can substitute for Docker is OCI standards compliance. Images follow the OCI Image Spec and registry communication follows the OCI Distribution Spec, so existing registries — Docker Hub, GitHub Container Registry, Amazon ECR, and so on — work unchanged. An image built from a Dockerfile behaves identically under Docker on a Linux server, under Kubernetes, or under any cloud runtime.
In other words, gradual adoption is possible: "local development on apple/container, deployment through the existing pipeline as before." Because images are compatible, the whole team does not have to switch at once either.
Performance and Security Trade-offs
Let us draw up the balance sheet of the one-VM-per-container model.
| Perspective | Single shared VM | One VM per container |
|---|---|---|
| Isolation strength | weak due to shared kernel | strong via hardware virtualization |
| Kernel CVE impact | spreads to all containers | confined to one VM |
| Memory overhead | one fixed VM preallocation | per-container kernel and init cost |
| Startup speed | VM already running | under one second including kernel boot |
| Idle cost | VM allocation always held | near zero with no containers |
| Inter-container traffic | fast within one kernel | goes through virtual network |
| Many containers at once | favored by shared resources | overhead accumulates per VM |
The isolation gain is unambiguous. Since no kernel is shared, the blast radius of a container escape vulnerability is limited to a single VM. This is the same logic that led AWS to put Lambda on Firecracker microVMs, and it matters especially in environments handling code with unclear trust boundaries — externally contributed CI jobs, AI agent output.
The memory overhead, meanwhile, is real. Each VM carries a guest kernel, page tables, and vminitd, adding a fixed cost of tens of megabytes per container. In the common dev scenario of two or three containers, total footprint is actually smaller than one big VM, but if you spin up twenty microservices simultaneously you should do the math on cumulative overhead. Memory ballooning reclaims memory from idle VMs but is not a silver bullet.
A Real-World Scenario — Building an AI Coding Agent Sandbox
Let us build the use case where stronger isolation shines brightest: having an AI coding agent build and test code from an external repository. Since you cannot know in advance what the agent will execute, the right pattern is to create a disposable VM per task and discard it without a trace when done.
#!/bin/bash
# agent-sandbox.sh — create a disposable isolated container per agent task
set -euo pipefail
TASK_ID="$1"
REPO_URL="$2"
SANDBOX_NAME="agent-${TASK_ID}"
# 1. Create a task-scoped volume (for retrieving artifacts)
container volume create "${SANDBOX_NAME}-out"
# 2. Run a disposable container with limited resources
# - do NOT mount any host directory (blocks home directory access)
# - attach only the artifact volume as writable
container run --rm \
--name "${SANDBOX_NAME}" \
--cpus 2 --memory 4g \
--volume "${SANDBOX_NAME}-out:/out" \
ubuntu:24.04 \
bash -c "git clone ${REPO_URL} /src && cd /src && make test 2>&1 | tee /out/test.log"
# 3. Retrieve only the results, then clean up the volume
container run --rm --volume "${SANDBOX_NAME}-out:/out" alpine:latest cat /out/test.log
container volume delete "${SANDBOX_NAME}-out"
Three things are essential in this pattern. First, even if agent code escapes the container, what it reaches is merely a minimal disposable guest kernel — not other tasks, not the host. Second, by mounting no host filesystem at all, you cut off the exfiltration path for secrets. Third, with the rm option on the run command, the VM and the container vanish together the moment the task ends, so no residual state accumulates. The decisive difference is that the single shared VM model simply cannot provide the first guarantee.
Operational Tips and Troubleshooting
Here are the operational issues you will encounter after a few months of use, and their remedies.
Managing Disk Usage
Images and builder caches accumulate. Periodic cleanup is advisable.
# Prune unused images
container image prune
# Stop and restart the builder VM (effectively resets the cache)
container builder stop
container builder delete
container builder start --cpus 4 --memory 8g
Private Registry Authentication Issues
Private registries in corporate environments suffer frequent token expiry. Suspect your login state first.
# Refresh credentials
container registry login registry.internal.example.com
# If pulls fail, check the cause with verbose logging
container --debug image pull registry.internal.example.com/team/app:latest
When the System Service Stops Responding
If the helper processes get wedged, restart the system service. Note that running containers will be terminated.
container system stop
container system start
container system logs # inspect logs if the problem recurs
Keeping an Eye on Memory Usage
Under the one-VM-per-container model, it is a good habit to check total memory footprint occasionally. Specifying per-container allocations improves predictability.
# Make a habit of specifying per-container resources
container run -d --name api --cpus 2 --memory 1g myapp:dev
# Check running containers and their state
container list --all
Checklist Before Migrating from Existing Tools
Before moving from Docker Desktop or Colima to apple/container, review the following.
- Assess compose dependence. If you manage multi-container stacks with docker-compose.yml, there is no direct counterpart today. Either unroll into scripts that run containers individually, or keep the existing tool for compose-mandatory projects.
- Check tools that depend on the Docker API, such as Testcontainers. Test libraries that talk to the Docker socket API directly may not work. Audit your integration test setup first.
- Check macOS versions. On macOS 15 some networking features (per-container IPs, user-defined networks) are restricted. Best to confirm everyone on the team is on macOS 26 or later.
- Inventory amd64-dependent images. Rosetta covers most cases, but images relying on kernel modules or exotic instructions should be tested in advance.
- Consistency with CI. If local is apple/container and CI is Linux Docker, image compatibility is fine, but differences in networking and volume behavior can affect test outcomes.
- Need for a local Kubernetes cluster. If you use tools like kind or minikube that build clusters on top of Docker, keeping the existing environment is the realistic choice for now.
- Plan gradual adoption. Since images are OCI compatible, a two-phase strategy is safe: move builds and single-container runs first, then migrate multi-container dev environments slowly.
Limitations and Critical Perspectives
For balance, here are the critical viewpoints.
Absence of Linux-only capabilities. With single-VM tools, the inside of the VM is a full Linux system, enabling privileged containers, kernel module loading, and eBPF debugging. In the per-container microVM model, the guest kernel is a minimal build, constraining these advanced scenarios. Container Machine mode fills part of the gap, but it is a separate flow from container mode.
The compose ecosystem void. In modern development workflows, compose is a de facto standard. Losing the declarative "one db, one cache, one app" experience and enumerating CLI commands instead can feel like a clear regression. Community compatibility layers are in progress, but official support does not exist yet.
Apple ecosystem lock-in. The tool is Apple Silicon only and tracks macOS versions. If your team includes Linux desktop users, it cannot be the common tool. Skepticism about how actively Apple maintains open source projects long-term is also raised repeatedly on Hacker News.
Maturity. Docker Desktop embodies a decade of edge case fixes. A young tool needs more time to be hardened against the messy conditions of the real world: registry auth schemes, proxy environments, corporate VPNs.
Outlook
Even so, the direction seems clear. First, stronger isolation is what the times demand. As supply chain attacks and AI agent sandboxing needs grow, the value of hardware virtualization isolation rises. Second, the very fact that the OS vendor ships a container runtime is a game changer. Recall what changed when WSL landed on Windows — Container Machine has the potential to change the default macOS development environment. Third, since Containerization is published as a library, an ecosystem reshuffle where third-party tools adopt it as a backend is possible. Discussions are indeed circulating in the community about existing tools evaluating it as a backend option.
For the near term, the realistic optimum is a hybrid: "builds and isolated runs on apple/container, compose stacks on existing tools." But the moment the compose gap is filled, the center of gravity could shift quickly.
Closing
apple/container does not hide the old truth that "containers on macOS are ultimately VMs" — it attacks it head-on by making VMs as light as containers. The one-lightweight-VM-per-container design beats the single-VM model on isolation and idle cost, while Rosetta and OCI compatibility keep it practical. The Container Machine of WWDC26 adds the everyday development environment to its territory.
Homework remains, of course: the compose ecosystem and advanced Linux features. It is hard to recommend a full switch today, but for single-container workflows and sandboxing untrusted code, it is already production-ready. Before your next Docker Desktop renewal season, install it and run container run once. The one second in which a kernel boots tells you more than you might expect.
References
- apple/container GitHub repository: https://github.com/apple/container
- apple/containerization GitHub repository: https://github.com/apple/containerization
- GeekNews topic: https://news.hada.io/topic?id=30346
- Apple Virtualization.framework documentation: https://developer.apple.com/documentation/virtualization
- apple/container official tutorial: https://github.com/apple/container/blob/main/docs/tutorial.md
- OCI Image Format Specification: https://github.com/opencontainers/image-spec
- OCI Distribution Specification: https://github.com/opencontainers/distribution-spec
- Hacker News discussion of the apple/container release: https://news.ycombinator.com/item?id=44229348
- Lima project: https://github.com/lima-vm/lima
- Colima project: https://github.com/abiosoft/colima
- Firecracker microVM (isolation model reference): https://firecracker-microvm.github.io/