Architecture of the Apple Container Tool: Inside macOS's OCI-Compatible Runtime

The Apple container tool implements a multi-layered Swift architecture where a CLI client communicates via XPC to a privileged daemon, which orchestrates per-container Linux VMs using Apple's Virtualization framework alongside specialized helpers for image operations and vmnet-based networking.

The apple/container repository provides a native container runtime designed specifically for macOS 26 on Apple Silicon. Unlike traditional container implementations, this tool integrates deeply with macOS-native frameworks including Virtualization, vmnet, and XPC services to deliver an OCI-compatible experience through a multi-process design that strictly separates user interfaces from privileged system operations.

CLI and XPC Communication Layer

The user-facing interface relies on XPC (Inter-Process Communication) to bridge the gap between the unprivileged CLI and the system daemon. When you execute commands like container run, the tool constructs an XPCMessage and transmits it to the daemon via the com.apple.container.xpc.route endpoint.

In Sources/ContainerXPC/XPCMessage.swift, the XPCMessage struct encapsulates both the route identifier and payload data. The server implementation in Sources/ContainerXPC/XPCServer.swift maintains a route table mapping string identifiers to async handler closures. A typical container creation route appears as:

let routes: [String: XPCServer.RouteHandler] = [
    "container.create": XPCServer.route { message in
        // decode request, spin up a VM, return container ID
    },
    // … other routes …
]

The server validates the client's EUID, dispatches the request to the appropriate handler, and returns either a success message or a ContainerizationError encoded via ContainerXPCError.

Daemon and Service Architecture

The container-apiserver runs as a Launch Daemon, providing the privileged backend that manages container lifecycle operations. This daemon owns the XPC server and coordinates communication between the CLI and various helper processes.

Launchd integration allows the system to start and stop the daemon automatically via container system start/stop commands. The daemon's responsibilities include launching per-container helpers, managing the OCI-compatible content store, and coordinating network configuration through the vmnet framework.

Per-Container Runtime Architecture

For each container, the daemon spawns a container-runtime-linux helper binary that executes inside a lightweight Linux VM. The Containerization Swift package wraps Apple's Virtualization framework to create these isolated execution environments.

Each VM runs a minimal Linux kernel with a user-space init process that forwards container-specific syscalls to the host. The SocketForwarder module, implemented in Sources/SocketForwarder/TCPForwarder.swift and its UDP counterpart, bridges network traffic between the host and container VM:

import SocketForwarder

let forwarder = TCPForwarder(
    listenPort: 8080,
    destinationHost: "127.0.0.1",
    destinationPort: 80
)
try forwarder.start()
print("Forwarding 0.0.0.0:8080 → 127.0.0.1:80")

Image Management and Storage

Image operations are handled by the container-core-images helper, which interacts directly with the OCI content store located at ~/.container. This component manages pull, push, and caching operations while storing registry credentials securely in the macOS Keychain.

The workflow follows this sequence:

  1. CLI sends an XPC request with route image.pull to the daemon
  2. The daemon forwards to container-core-images, which authenticates via Keychain
  3. The helper downloads manifest layers and stores them in the local OCI content store
  4. An OCI-compatible image reference returns to the daemon for use by the runtime VM

Virtual Networking Stack

Networking leverages the vmnet framework through the container-network-vmnet helper. This component creates virtual network interfaces and assigns IP addresses to each container VM.

On macOS 26, the architecture supports multiple isolated networks, while macOS 15 restricts operations to a single default network. The SocketForwarder facilitates TCP and UDP proxying between the host network stack and the container's virtual interface.

Configuration System

All daemon and helper behavior is driven by a TOML configuration file parsed into Swift structs defined in Sources/ContainerPersistence/ContainerSystemConfig.swift. The configuration hierarchy includes:

  • BuildConfig – Builder image specifications and resource limits
  • ContainerConfig – Default CPU and memory allocations per container
  • NetworkConfig – Optional subnet overrides for vmnet
  • KernelConfig – Path and URL for the Linux kernel binary
  • VminitConfig – Vminit helper image settings

The system provides sensible defaults for all fields, making the tool usable without manual configuration while allowing granular customization through config.toml:

import ContainerPersistence

let configURL = URL(fileURLWithPath: "\(NSHomeDirectory())/.container/config.toml")
let loader = ConfigurationLoader()
let systemConfig = try loader.load(from: configURL)
print("Default container memory: \(systemConfig.container.memory)")

Programmatic XPC Client Usage

Developers can interact with the daemon directly using the same XPC facilities as the CLI:

import ContainerXPC
import Foundation

let client = XPCClient(identifier: "com.apple.container.apiserver")
let request = XPCMessage(route: "container.create")

request.set(key: "image", value: "docker.io/library/nginx:latest")
request.set(key: "command", value: ["/usr/sbin/nginx", "-g", "daemon off;"])

Task {
    do {
        let response = try await client.send(request)
        try response.error()
        let containerID = response.string(key: "containerID")
        print("Container started: \(containerID ?? "unknown")")
    } catch {
        print("Failed: \(error)")
    }
}

Summary

  • The Apple container tool uses a multi-process architecture separating the CLI, daemon, and per-container helpers via XPC communication
  • The daemon (container-apiserver) runs as a Launch Daemon and coordinates image pulls, networking, and VM lifecycle through specialized helpers
  • Each container runs in a lightweight Linux VM using the Virtualization framework, with container-runtime-linux handling execution
  • Image management occurs through container-core-images, which maintains an OCI-compatible content store at ~/.container and uses Keychain for credentials
  • Networking is provided by container-network-vmnet using the vmnet framework, with SocketForwarder bridging host and container traffic
  • Configuration is managed through ContainerSystemConfig.swift, parsing config.toml into type-safe Swift structs with sensible defaults

Frequently Asked Questions

How does the Apple container tool differ from Docker Desktop?

The Apple container tool integrates directly with macOS-native frameworks like Virtualization, vmnet, and XPC services rather than relying on a traditional Linux VM or virtual machine manager. According to Sources/ContainerXPC/XPCServer.swift, it uses a privileged daemon architecture with per-container helpers, whereas Docker typically uses a single privileged VM running Linux.

What is the purpose of the XPC layer in the container architecture?

The XPC layer provides secure inter-process communication between the unprivileged CLI and the privileged container-apiserver daemon. As implemented in Sources/ContainerXPC/XPCMessage.swift, it validates client EUIDs and routes requests through a structured message protocol, ensuring that privileged operations like VM creation and network configuration are properly authorized.

Where does the tool store container images and configuration?

The tool maintains an OCI-compatible content store under ~/.container for image layers and manifests, as handled by container-core-images. System-wide configuration is parsed from config.toml into ContainerSystemConfig structs defined in Sources/ContainerPersistence/ContainerSystemConfig.swift, while registry credentials are stored securely in the macOS Keychain.

Can I run the Apple container tool on macOS versions earlier than 26?

While the tool may function on earlier versions, the architecture specifically supports multiple isolated networks on macOS 26, whereas macOS 15 restricts networking to a single default network. The Virtualization framework requirements and daemon architecture are optimized for macOS 26 and Apple Silicon hardware.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →