# Architecture of the Machine API Server in apple/container: Plugin-Based XPC Design

> Explore the apple/container Machine API server architecture. Learn about its plugin-based XPC design and four core components for managing container VMs.

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

---

**The Machine API Server in apple/container is implemented as a standalone plugin process that exposes an XPC-based RPC interface, comprising four core components—MachineAPIServer, MachinesService, MachinesHarness, and persistent MachineBundles—to manage container-based virtual machines.**

The apple/container repository provides a container runtime with virtual machine capabilities. The **Machine API Server** serves as the central nervous system for managing container-based VMs, exposing a robust RPC interface that decouples the CLI from the underlying container runtime. This architecture leverages Swift’s actor model and XPC communication to provide safe, concurrent access to machine lifecycle operations.

## Core Architecture Components

The Machine API Server architecture consists of four tightly integrated components that handle everything from command-line parsing to persistent state management.

### MachineAPIServer Entry Point

The **`MachineAPIServer`** struct in [`Sources/Plugins/MachineAPIServer/MachineAPIServer.swift`](https://github.com/apple/container/blob/main/Sources/Plugins/MachineAPIServer/MachineAPIServer.swift) declares the top-level command (`machine-apiserver`) and wires the `Start` sub-command. This plugin entry point integrates with the container framework’s command structure, allowing the server to be installed alongside other container plugins.

### Start Command and Initialization

The **`MachineAPIServer.Start`** implementation in `Sources/Plugins/MachineAPIServer/MachineAPIServer+Start.swift` handles the bootstrap sequence. The `run()` method parses CLI flags, creates a structured logger, and constructs a `MachinesService` instance using two critical paths: `appRoot` (the plugin state directory) and `resourceRoot` (supplied by the CLI). It then launches the XPC server, registering routes that map RPC methods to the service layer.

### MachinesService Actor

The **`MachinesService`** actor in [`Sources/Services/MachineAPIService/Server/MachinesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Server/MachinesService.swift) forms the heart of the API. This Swift actor owns persistent machine bundles, coordinates concurrent access via an `AsyncLock`, and communicates with the container runtime through `ContainerClient`. It tracks running containers through an `ExitMonitor` and implements the full lifecycle: `list`, `create`, `delete`, `boot`, `stop`, `inspect`, `setConfig`, and default-machine handling.

The service maintains an in-memory dictionary `machines: [String: MachineState]` loaded from persistent bundles at startup. All mutating operations execute inside `lock.withLock { … }` blocks to serialize access and guarantee consistency during concurrent XPC requests.

### MachinesHarness XPC Adapter

The **`MachinesHarness`** in [`Sources/Services/MachineAPIService/Server/MachinesHarness.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Server/MachinesHarness.swift) serves as a thin XPC façade that unmarshals `XPCMessage` objects, converts them to Swift types, forwards calls to `MachinesService`, and encodes JSON replies. This adapter decouples the transport layer from business logic, allowing the service to remain pure Swift while handling XPC-specific serialization concerns.

## Data Flow and Request Lifecycle

Understanding the request path clarifies how components interact during machine operations.

1. **Startup**: When launched via `machine-apiserver start`, the `Start.run()` method initializes `MachinesService` and registers XPC routes on the identifier `MachineClient.serviceIdentifier`.

2. **XPC Routing**: The server listens for commands like `listMachine`, `createMachine`, and `bootMachine`. Each route points to a corresponding method on `MachinesHarness`.

3. **Request Handling**: `MachinesHarness` extracts JSON-encoded payloads (such as `MachineConfiguration` or `MachineResources`), invokes the matching `MachinesService` method, and returns JSON or file descriptors.

4. **Machine State**: The service loads machine bundles from `<appRoot>/machines` directories. Each bundle contains the image filesystem, configuration files, and log files.

5. **Container Interaction**: 
   - `create` prepares a `MachineBundle`, copies the rootfs from the image, and stores a snapshot with status **stopped**.
   - `boot` converts `MachineConfiguration` to `ContainerConfiguration` via `MachineConfiguration.toContainerConfig`, then asks `ContainerClient` to create and start the container. It spawns background tasks piping stdout/stderr to log files and registers an `ExitMonitor` callback.
   - `stop` forwards the request to `ContainerClient` and updates the snapshot.
   - `inspect` merges bundle metadata (disk size, initialization flag) with live container info (IP address).

6. **Concurrency Control**: All mutating operations run inside `lock.withLock { … }` to serialize access, ensuring consistency even when multiple XPC clients issue concurrent commands.

7. **Default Machine**: The service stores the default machine name in [`state.json`](https://github.com/apple/container/blob/main/state.json). Calls to `getDefault`, `setDefault`, and parameterless `boot` reference this value.

## Persistence and State Management

The architecture employs a dual-layer state strategy combining in-memory actors with filesystem persistence.

**Machine Bundles**: Persistent storage lives under `<pluginStateRoot>/machines`. Each machine bundle contains the rootfs, `stdioLog`, `bootLog`, and metadata. The `MachineBundle` class in [`Sources/ContainerPersistence/MachineBundle.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/MachineBundle.swift) manages this on-disk representation.

**Exit Monitoring**: The `ExitMonitor` registers per-machine closures that execute when backing containers exit. This ensures snapshots reset to `stopped`, log pipelines terminate, and resources cleanup occurs automatically.

**Default Machine Tracking**: A simple JSON file ([`state.json`](https://github.com/apple/container/blob/main/state.json)) in the plugin directory tracks the default machine name, enabling commands like `container-machine boot` without explicit `--name` flags.

## CLI Integration and Usage Examples

The `container-machine` CLI commands communicate with this XPC server transparently. Below are typical usage patterns that exercise the API:

```bash

# Start the Machine API helper (normally launched automatically by the daemon)

container-machine-apiserver start --resources /usr/local/share/container/resources

# Create a new machine named "dev-vm" from the alpine image

container-machine create --name dev-vm alpine:latest

# List all machines (shows ID, status, IP, etc.)

container-machine list

# Boot the default machine (or specify an ID)

container-machine boot --name dev-vm

# Inspect a running machine

container-machine inspect dev-vm

# Stop a running machine

container-machine stop dev-vm

# Delete a stopped machine

container-machine delete dev-vm

# Set a machine as the default (used when no --name is supplied)

container-machine set-default dev-vm

```

For direct API access from Swift (e.g., from another plugin), use the XPC client:

```swift
import MachineAPIClient
import ContainerXPC

let client = MachineClient()
let listMessage = XPCMessage(command: MachineRoutes.listMachine.rawValue)
let reply = try await client.send(listMessage)
let machinesData = reply.data(key: MachineKeys.machines.rawValue)!
let machines = try JSONDecoder().decode([MachineSnapshot].self, from: machinesData)
print("Machines:", machines)

```

## Summary

- The Machine API Server runs as a **plugin process** with a standalone XPC interface, enabling decoupled communication between CLI tools and the container runtime.
- **Four core components**—`MachineAPIServer`, `Start`, `MachinesService`, and `MachinesHarness`—handle command parsing, initialization, business logic, and RPC adaptation respectively.
- **Swift actors and AsyncLock** provide thread-safe concurrent access to machine state, while `ExitMonitor` ensures proper cleanup when containers terminate.
- **Machine bundles** persist under `<appRoot>/machines`, storing filesystems, logs, and metadata, with [`state.json`](https://github.com/apple/container/blob/main/state.json) tracking the default machine.
- The architecture supports both CLI interaction via `container-machine` commands and programmatic access through `MachineClient` XPC messages.

## Frequently Asked Questions

### What is the role of the MachinesService actor in the Machine API Server?

The **`MachinesService`** actor is the core business logic layer that manages the complete lifecycle of container-based virtual machines. It maintains an in-memory dictionary of machine states, coordinates concurrent access using an `AsyncLock`, and interfaces with the container runtime through `ContainerClient`. According to the source code in [`Sources/Services/MachineAPIService/Server/MachinesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Server/MachinesService.swift), it implements all RPC methods including `create`, `boot`, `stop`, and `inspect`, while loading persistent state from machine bundles at startup.

### How does the Machine API Server handle concurrent requests?

The server handles concurrency through Swift’s **actor model** and explicit locking. The `MachinesService` is declared as an `actor`, providing compile-time data-race protection. Additionally, all mutating operations execute inside `lock.withLock { … }` blocks to serialize access to the shared `machines` dictionary. This ensures that concurrent XPC requests from multiple clients cannot corrupt machine state during operations like `boot` or `delete`.

### What is the difference between MachinesService and MachinesHarness?

**`MachinesHarness`** acts as a thin XPC adapter that unmarshals incoming `XPCMessage` objects and converts them to Swift types before forwarding calls to **`MachinesService`**. While `MachinesHarness` handles transport-specific concerns like JSON encoding and XPC reply formatting in [`Sources/Services/MachineAPIService/Server/MachinesHarness.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Server/MachinesHarness.swift), `MachinesService` contains the actual business logic for container lifecycle management. This separation keeps the service layer clean and testable while isolating RPC implementation details.

### How are machine states persisted in the apple/container architecture?

Machine states persist through **machine bundles** stored under `<pluginStateRoot>/machines`, as implemented in [`Sources/ContainerPersistence/MachineBundle.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/MachineBundle.swift). Each bundle contains the VM filesystem, configuration snapshots, and log files (`stdioLog`, `bootLog`). The `MachinesService` loads these bundles into memory at startup and writes updates back to disk during lifecycle transitions. Additionally, a [`state.json`](https://github.com/apple/container/blob/main/state.json) file tracks the default machine name, enabling implicit default machine selection in CLI commands.