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

> Explore the Apple container tool architecture a multi layered Swift design enabling OCI compatible runtimes on macOS via Linux VMs and the Virtualization framework.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: architecture
- Published: 2026-07-03

---

**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`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCMessage.swift), the `XPCMessage` struct encapsulates both the route identifier and payload data. The server implementation in [`Sources/ContainerXPC/XPCServer.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCServer.swift) maintains a route table mapping string identifiers to async handler closures. A typical container creation route appears as:

```swift
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`](https://github.com/apple/container/blob/main/Sources/SocketForwarder/TCPForwarder.swift) and its UDP counterpart, bridges network traffic between the host and container VM:

```swift
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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/config.toml):

```swift
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:

```swift
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`](https://github.com/apple/container/blob/main/ContainerSystemConfig.swift), parsing [`config.toml`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/config.toml) into `ContainerSystemConfig` structs defined in [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/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.