Apple Container Architecture: How macOS Runs OCI Containers in Lightweight VMs
TLDR: Apple's container architecture implements a layered system that combines a CLI frontend, XPC-based helper services, and per-container lightweight Linux VMs orchestrated by the macOS Virtualization framework to provide hardware-level isolation for OCI-compatible workloads.
The apple/container repository delivers a native macOS container runtime that bridges OCI standards with macOS frameworks. Unlike traditional container engines that share the host kernel, this architecture spins up a dedicated lightweight virtual machine for every container, ensuring complete kernel and filesystem isolation while maintaining fast startup times through efficient XPC communication. According to the technical documentation in docs/technical-overview.md, the system delegates specific responsibilities to specialized XPC helpers that interface with macOS-native services.
CLI and API Server Layer
The user-facing component is the container command-line interface, which parses commands and communicates with the backend through GRPC-style APIs.
The container Binary
The container binary handles argument parsing, user output, and XPC connections to the backend services. It serves as the primary entry point for all container operations.
The container-apiserver Service
When you execute container system start, the tool launches container-apiserver as a launch agent that registers with launchd. This central server, implemented in Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift, exposes the primary API surface for container, image, and network management. It routes requests to appropriate XPC helpers and integrates with Sources/ContainerLog/ServiceLogger.swift for unified system logging.
XPC Helper Services
The architecture delegates specialized tasks to three lightweight XPC services that run in their own sandboxes, communicating through protocols defined in Sources/ContainerXPC/XPCServerSession.swift.
container-core-images
This helper handles image store operations and registry access, authenticating via the macOS Keychain. It fetches OCI image layers and prepares them for streaming into the VM.
container-network-vmnet
This service configures virtual network interfaces using the vmnet framework. It creates a virtual Ethernet interface for each VM and manages NAT-style networking.
container-runtime-linux
This helper manages the per-container lifecycle inside lightweight VMs. The implementation in Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift requests VM creation from the Virtualization framework and coordinates the container init process.
Virtualization and Isolation Layer
Each container runs inside a dedicated lightweight Linux VM orchestrated by the macOS Virtualization framework.
VM Creation and Lifecycle
The container-runtime-linux helper requests VM creation from the framework, passing configuration parameters such as kernel images and memory limits. The VM isolates the container's kernel, networking stack, and filesystem from the host, exposing only explicitly mounted host paths to reduce the attack surface.
Security Boundaries
The VMs use minimal runtime libraries to ensure fast startup while maintaining VM-level isolation. XPC sandboxing ensures that each helper operates with limited privileges, and only files explicitly mounted by the user are visible inside the VM.
Networking Architecture
Network isolation relies on the vmnet framework available in macOS 15 and later. The container-network-vmnet helper creates a virtual Ethernet interface for each VM, connecting it to a NAT-style network with the default subnet 192.168.64.0/24.
While macOS 15 offers limited isolation, macOS 26+ supports full VMnet-based networks with enhanced security boundaries. The CLI inspects these configurations by querying the XPC helper, which interfaces directly with the framework's virtual switch.
Configuration Management
System defaults are managed by the ContainerSystemConfig Swift struct in Sources/ContainerPersistence/ContainerSystemConfig.swift. This structure reads TOML configuration files at startup to define VM kernel paths, memory limits, DNS settings, and networking parameters. The configuration persists across reboots and provides the central source of truth for the container-apiserver when creating new container instances.
Request Lifecycle and Data Flow
A typical container run command traverses seven distinct layers:
- The CLI parses arguments and sends a request to
container-apiservervia XPC. - The API server validates the OCI image specification and creates an XPC client for
container-runtime-linux. - The runtime helper requests VM creation from the Virtualization framework.
- The framework launches a minimal Linux VM with the specified kernel and init process.
- The VM's network interface attaches to
container-network-vmnet, which configures thevmnetvirtual Ethernet adapter. container-core-imagesfetches image layers from the registry (using Keychain-stored credentials) and streams the filesystem into the VM.- The container process executes inside the VM, with stdout/stderr routed back through the XPC helpers to the CLI for display.
This flow ensures that heavy I/O operations like image pulling happen outside the VM, while the actual workload runs inside hardware-isolated boundaries.
Practical Code Examples
Starting a Container via Swift Client
The following Swift code demonstrates how the client library interacts with the API server, defined in Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift:
import ContainerAPIClient
import ContainerRuntimeClient
import ContainerXPC
let client = try ContainerAPIClient()
let spec = ContainerSpec(
image: "docker.io/library/alpine:latest",
command: ["/bin/sh", "-c", "echo Hello from container"]
)
// Ask the server to create and start the container
let containerID = try client.runContainer(spec: spec)
// Stream its output
let stream = try client.attach(to: containerID, stdOut: true, stdErr: true)
for try await line in stream.lines {
print(line)
}
Minimal XPC Server Implementation
The container-runtime-linux helper implements a protocol similar to this stub, based on the actual implementation in Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift:
import ContainerXPC
import ContainerRuntimeLinuxServer
class RuntimeServer: NSObject, ContainerRuntimeLinuxServerProtocol {
func startContainer(_ request: StartRequest,
withReply reply: @escaping (StartResponse) -> Void) {
// Launch the lightweight VM via the Virtualization framework
let vm = LightweightLinuxVM(configuration: request.vmConfig)
vm.start()
reply(StartResponse(success: true))
}
}
// Register the service
let listener = XPCListener(serviceName: "com.apple.container.runtime.linux")
listener.delegate = RuntimeServer()
listener.resume()
Inspecting Network Configuration
You can verify the vmnet-based network configuration using the CLI:
$ container network inspect
{
"subnet": "192.168.64.0/24",
"gateway": "192.168.64.1",
"driver": "vmnet"
}
This command forwards the request to the container-network-vmnet XPC helper, which queries the vmnet framework for current interface states.
Summary
- Apple container architecture uses a modular, XPC-driven design defined in
Package.swiftto orchestrate OCI containers inside lightweight Linux VMs. - The Virtualization framework provides hardware-level isolation for each container, while XPC helpers (
container-core-images,container-network-vmnet,container-runtime-linux) handle specialized tasks. - Configuration is centralized in
ContainerSystemConfigand persisted via TOML files inSources/ContainerPersistence/. - Networking leverages the vmnet framework with NAT-style subnets, requiring macOS 15 or later.
- Communication flows from CLI → API Server → XPC Helpers → VM, ensuring that only explicitly mounted host paths are visible inside containers.
Frequently Asked Questions
How does Apple Container architecture differ from Docker Desktop?
Unlike Docker Desktop, which uses a single Linux VM to host all containers, Apple's architecture creates a dedicated lightweight VM for each container. This provides stronger isolation at the kernel level, as each container runs with its own Linux kernel instance rather than sharing the VM's kernel. The XPC-based communication layer also integrates more deeply with macOS security frameworks like Keychain and Unified Logging.
What macOS versions support the Apple Container architecture?
The architecture requires macOS 15 or later for basic functionality, specifically for the vmnet framework support. macOS 26 and later versions provide enhanced networking isolation with full VMnet-based network capabilities, while earlier versions have limited network isolation between containers.
Where are the XPC service definitions located in the source code?
The core XPC session handling is implemented in Sources/ContainerXPC/XPCServerSession.swift, which defines the communication protocols used by all helpers. The container-runtime-linux specific implementation resides in Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift, while the API server definitions are in Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift.
How does the system handle registry authentication?
The container-core-images XPC helper retrieves registry credentials from the macOS Keychain, leveraging the platform's secure credential storage. This eliminates the need for plaintext credential files, with authentication handled transparently during image pull operations in step six of the request lifecycle.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →