# How apple/container Manages XPC Services for Interprocess Communication: A Deep Dive into ContainerXPC

> Discover how apple/container uses ContainerXPC to manage XPC services for robust inter-process communication between VM-backed runtimes and clients.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: deep-dive
- Published: 2026-07-02

---

**The apple/container repository uses the ContainerXPC library to expose a full-featured XPC service via the `RuntimeService` actor, which mediates all communication between the VM-backed container runtime and client applications through typed message contracts, anonymous endpoints, and serialized state management.**

The apple/container project implements a secure, asynchronous IPC layer using XPC services to bridge the container runtime with external clients. At the heart of this system sits the **ContainerXPC** library, which abstracts low-level XPC connections into Swift-native constructs that handle everything from VM bootstrapping to process lifecycle management. Understanding this architecture reveals how Apple structures type-safe, actor-based interprocess communication for containerized workloads.

## The ContainerXPC Architecture

The IPC stack centers on three core components that isolate XPC plumbing from business logic.

**RuntimeService** acts as the server-side **actor** that owns an `xpc_connection_t` originally created by the launchd daemon. It registers handlers for XPC routes defined in `RuntimeRoutes` and maintains serialized access to internal state including VM processes and socket forwarders.

**RuntimeClient** provides the client-side Swift API that constructs `XPCClient` instances, negotiates endpoints with the service, and exposes high-level methods for container management.

**RuntimeRoutes** enumerates the exact string identifiers for every remote procedure call supported by the service, ensuring type-safe method dispatch across the XPC boundary.

## Service Implementation with RuntimeService

Located in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift), the `RuntimeService` actor serves as the primary entry point for all XPC traffic.

The service initializes by wrapping an `xpc_connection_t` and immediately registering route handlers. Because `RuntimeService` is declared as a Swift `actor`, the compiler guarantees serialized access to its mutable state, including the `processes` dictionary and `socketForwarders` array. For additional fine-grained control over critical sections—such as during `bootstrap` or `startProcess` operations—the implementation uses an `AsyncLock` declared as `private let lock = AsyncLock()`.

Concurrency safety extends to endpoint creation. The `createEndpoint` method (lines 21-28) generates an anonymous XPC endpoint using `xpc_endpoint_create` and returns it to clients, establishing isolated communication channels for subsequent operations.

## Defining XPC Routes and Message Contracts

Communication contracts reside in [`Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift`](https://github.com/apple/container/blob/main/Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift), where the `RuntimeRoutes` enum defines every supported XPC method string. These strings represent the exact selectors clients use when invoking remote procedures, covering operations like bootstrap, process creation, file copying, and shutdown.

Message serialization uses the `XPCMessage` type. The service reads parameters such as `id`, `stdio`, and `networkBootstrapInfos` via helper extensions defined within [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift) (lines 1276-1499). Responses are constructed using the `reply()` helper and populated with keys defined in `RuntimeKeys`, ensuring consistent key-value encoding across the wire.

Complex data structures—including `ProcessConfiguration`, `ContainerStopOptions`, and `NetworkBootstrapInfo`—serialize to JSON for transport, while raw file descriptors pass directly through XPC's binary-safe FD passing mechanism.

## Client-Side Communication with RuntimeClient

The `RuntimeClient` class in [`Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift) encapsulates the client-side XPC logistics.

Initialization follows a two-phase pattern. First, `RuntimeClient.create` (lines 50-75) initiates an XPC call to the service's `createEndpoint` method. Upon receiving the anonymous endpoint, the client constructs a new `xpc_connection_t` from that endpoint and returns a fully configured `RuntimeClient` instance ready for container operations.

This endpoint-per-client model ensures that each container session operates within an isolated XPC context, preventing crosstalk between different container instances while allowing the service to manage resources per connection.

## Lifecycle Management Over XPC

The XPC service exposes granular control over the container lifecycle through strongly-typed methods.

**Bootstrap**: The `bootstrap` method accepts `stdio` file handles and `networkBootstrapInfos` arrays, initializes the VM, configures networking via `ContainerNetworkClient`, stores resulting `XPCClientSession` objects, and launches the init process.

**Process Management**: Clients invoke `createProcess`, `startProcess`, `kill`, `resize`, and `wait` through the XPC boundary. The service forwards these requests to the running container or init process, with each operation protected by the actor's serialization guarantees.

**Network Sessions**: During bootstrap, the service opens `ContainerNetworkClient` connections for each network attachment (lines 74-86) and stores active sessions. The `cleanUpContainer` method (lines 68-70) closes these sessions during shutdown.

**Shutdown**: The `shutdown` method gracefully stops the VM, removes socket forwarders, and invalidates the XPC session.

## Practical Implementation Examples

The following examples demonstrate common XPC interactions using the apple/container API.

Creating a RuntimeClient and bootstrapping a sandbox:

```swift
import ContainerXPC
import ContainerRuntimeClient
import TerminalProgress

// Create client - asks service for endpoint (RuntimeClient.swift lines 50-75)
let client = try await RuntimeClient.create(
    id: "my-container",
    runtime: "default"
)

let stdio: [FileHandle?] = [
    FileHandle.standardInput,
    FileHandle.standardOutput,
    FileHandle.standardError
]

let netInfos: [NetworkBootstrapInfo] = [
    .init(plugin: .vmnet, interface: .default)
)

// Bootstrap the VM (RuntimeService.swift bootstrap)
try await client.bootstrap(
    stdio: stdio,
    networkBootstrapInfos: netInfos,
    dynamicEnv: ProcessInfo.processInfo.environment
)

```

Starting a process inside the container:

```swift
// Register process (RuntimeClient.createProcess lines 34-55)
try await client.createProcess(
    "my-process",
    config: ProcessConfiguration(
        executable: "/bin/ls",
        arguments: ["-la"],
        environment: [:]
    ),
    stdio: stdio
)

// Start execution (RuntimeService.swift lines 413-456)
try await client.startProcess("my-process")

```

Waiting for process completion:

```swift
// Wait returns exit status (RuntimeClient.wait lines 31-48, RuntimeService.wait lines 660-678)
let exitStatus = try await client.wait("my-process")
print("Exit code:", exitStatus.exitCode)

```

Copying files into the container:

```swift
// File operations (RuntimeClient.copyIn lines 87-96, RuntimeService.copyIn lines 695-724)
try await client.copyIn(
    source: "/tmp/local.txt",
    destination: "/root/container.txt",
    mode: 0o644,
    createParents: true
)

```

Shutting down:

```swift
// Cleanup (RuntimeClient.shutdown lines 73-84, RuntimeService.shutdown lines 73-80)
try await client.shutdown()

```

## Error Handling and Type Safety

All XPC handlers in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift) throw **ContainerizationError** with rich contextual information. These errors propagate through the XPC boundary and map back to `ContainerizationError` on the client side in [`RuntimeClient.swift`](https://github.com/apple/container/blob/main/RuntimeClient.swift), preserving the complete error chain across process boundaries.

The architecture enforces type safety at the XPC layer. While simple values pass as XPC dictionaries, complex configuration objects use JSON encoding, ensuring that structures like `ProcessConfiguration` maintain integrity across the wire. Raw file handles leverage XPC's native descriptor passing rather than serialization, maintaining POSIX semantics for stdin/stdout/stderr redirection.

## Summary

- The **RuntimeService** actor in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift) centralizes XPC request handling with Swift-native concurrency safety.
- **RuntimeRoutes** defines the XPC method namespace, while **XPCMessage** extensions (lines 1276-1499) handle parameter extraction and reply construction.
- Clients obtain isolated endpoints via `createEndpoint` (lines 21-28), then communicate through `RuntimeClient` instances that manage the underlying `xpc_connection_t`.
- The system supports complete container lifecycle management—bootstrap, process control, file I/O, and shutdown—over typed XPC messages.
- **AsyncLock** complements the actor model for fine-grained synchronization during critical operations like VM initialization.

## Frequently Asked Questions

### How does apple/container establish the initial XPC connection between client and service?

The client calls `RuntimeClient.create`, which sends an XPC request to the service's `createEndpoint` method (lines 21-28 of [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift)). This creates an anonymous endpoint using `xpc_endpoint_create` that the service returns to the client. The client then constructs a dedicated `xpc_connection_t` from this endpoint, establishing an isolated communication channel for that specific container session.

### What concurrency mechanisms protect shared state in the XPC service?

The `RuntimeService` is implemented as a Swift `actor`, which serializes access to its internal state properties like `processes` and `socketForwarders`. For operations requiring explicit critical section management—such as `bootstrap` and `startProcess`—the code uses an `AsyncLock` (`private let lock = AsyncLock()`) to prevent race conditions during VM initialization and process spawning.

### How are complex data structures passed over XPC in apple/container?

Complex types including `ProcessConfiguration`, `NetworkBootstrapInfo`, and `ContainerStopOptions` serialize to JSON for transport over XPC. Simple parameters extract directly from `XPCMessage` via helper extensions (lines 1276-1499 in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift)). Raw file descriptors pass through XPC's native binary-safe FD passing mechanism rather than serialization, preserving handle semantics for standard I/O redirection.

### Where does the service handle network session lifecycle during container operations?

During bootstrap (lines 74-86 of [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift)), the service instantiates `ContainerNetworkClient` for each network attachment and stores the resulting `XPCClientSession` objects. These sessions remain active for the container's lifetime and are explicitly closed during the cleanup phase in `cleanUpContainer` (lines 68-70) when the container shuts down or encounters a fatal error.