# How to Use the Client API for Programmatic Container Management in Apple Container

> Learn to manage Apple Containers programmatically with the Swift client API. Automate container lifecycle operations asynchronously via XPC.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-07-11

---

**The Apple Container project exposes a Swift-based client API through the `ContainerAPIClient` module that enables fully asynchronous container lifecycle management via XPC communication with the container daemon.**

The `apple/container` repository provides a native macOS container runtime that you can control programmatically from your own Swift applications. By importing the `ContainerAPIClient` module, you gain access to type-safe methods for creating, managing, and monitoring containers without invoking command-line tools. This interface abstracts the underlying XPC transport into modern Swift concurrency patterns, making it straightforward to integrate container operations into your app's architecture.

## Architecture of the Client API

The programmatic interface is organized into three distinct layers that handle communication, command mapping, and data modeling.

### XPC Transport Layer

At the foundation, the **XPC Transport** manages inter-process communication between your client application and the container daemon. The `XPCClient` class initializes connections, while `XPCClientSession` maintains the lifecycle of individual request-response cycles. These components serialize Swift structs into XPC messages and deserialize daemon responses back into Swift types.

Key source files:
- [`Sources/ContainerXPC/XPCClient.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCClient.swift)
- [`Sources/ContainerXPC/XPCClientSession.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCClientSession.swift)

### Service Stubs and High-Level Interface

The `ContainerAPIClient` target provides the **service stubs** that expose high-level async methods mapping one-to-one to daemon RPC endpoints. Rather than manually constructing XPC messages, you call Swift methods like `pullImage(named:)` or `createContainer(with:)`, which return `Result` types or throw errors that propagate daemon-side failures.

This layer handles all containers, images, networks, volumes, machines, and system commands through a consistent async/await interface.

### Domain Models and Configuration

**Domain models** define the data structures exchanged with the daemon. Types such as `ContainerConfiguration`, `ImageReference`, and `NetworkAttachment` conform to `Codable`, enabling automatic serialization by the XPC layer. These models mirror the configuration options available in the CLI but provide compile-time safety and IDE autocomplete support.

Key source files:
- [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift)
- [`Sources/ContainerCommands/Container/ContainerCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerCreate.swift)

## Setting Up the Client API

To begin using the client API for programmatic container management, import the required modules and instantiate an `XPCClient`. The default client connects to the system daemon automatically, requiring no manual configuration for standard deployments.

```swift
import ContainerAPIClient
import ContainerXPC

// Initialize the default client connection
let client = XPCClient()

```

All operations are asynchronous and use Swift's structured concurrency, allowing you to integrate them seamlessly into `Task` groups or `async` function chains.

## Programmatic Container Management Examples

The following examples demonstrate common container operations using the `ContainerAPIClient` interface.

### Connecting to the Daemon

While `XPCClient` handles connection establishment automatically, you can verify connectivity by listing system information or checking daemon status before proceeding with resource-intensive operations.

### Pulling Images

Download container images programmatically using the `pullImage` method, which accepts a fully qualified image name and streams progress through the async interface.

```swift
Task {
    do {
        try await client.pullImage(named: "docker.io/library/alpine:latest")
        print("✅ Image pulled successfully")
    } catch {
        print("❌ Pull failed: \(error)")
    }
}

```

### Creating and Starting Containers

Construct a `ContainerConfiguration` instance to define runtime parameters, then pass it to `createContainer` to receive a unique container identifier. Start the container using the returned ID.

```swift
Task {
    do {
        let config = ContainerConfiguration(
            image: "docker.io/library/alpine:latest",
            command: ["/bin/sh", "-c", "while true; do echo hello; sleep 1; done"]
        )
        let containerID = try await client.createContainer(with: config)
        try await client.startContainer(id: containerID)
        print("🚀 Container started: \(containerID)")
    } catch {
        print("❌ Could not start container: \(error)")
    }
}

```

### Executing Commands in Containers

Run commands inside running containers using the `execInContainer` method, which captures stdout and stderr streams and returns them as structured data.

```swift
Task {
    do {
        let execResult = try await client.execInContainer(
            id: "my-container",
            command: ["cat", "/etc/os-release"]
        )
        print("📄 Exec output:\n\(execResult.stdout)")
    } catch {
        print("❌ Exec failed: \(error)")
    }
}

```

### Listing and Removing Containers

Manage container lifecycle by querying active containers and removing them when no longer needed. The `listContainers` method returns an array of metadata objects, while `removeContainer` accepts a force flag to terminate running instances.

```swift
Task {
    do {
        // List all containers
        let containers = try await client.listContainers()
        containers.forEach { print("\($0.id) – \($0.status)") }
        
        // Remove a specific container
        try await client.removeContainer(id: "my-container", force: true)
        print("🗑️ Container removed")
    } catch {
        print("❌ Operation failed: \(error)")
    }
}

```

## Key Source Files and References

The following files define the client API surface and provide implementation details for extending or debugging the programmatic interface:

- **[`Sources/ContainerXPC/XPCClient.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCClient.swift)** – Low-level XPC connection management and message serialization.
- **[`Sources/ContainerXPC/XPCClientSession.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCClientSession.swift)** – Session lifecycle handling and request multiplexing.
- **[`Sources/ContainerCommands/Container/ContainerCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerCreate.swift)** – `ContainerConfiguration` definitions and creation logic.
- **[`Sources/ContainerCommands/Image/ImagePull.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Image/ImagePull.swift)** – Image pulling and registry interaction methods.
- **[`Sources/ContainerCommands/Network/NetworkCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Network/NetworkCreate.swift)** – Network provisioning and attachment APIs.
- **[`Sources/ContainerCommands/Volume/VolumeCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Volume/VolumeCreate.swift)** – Persistent volume management operations.
- **[`Sources/ContainerCommands/Machine/MachineCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Machine/MachineCreate.swift)** – VM-based container machine controls.
- **[`Sources/ContainerCommands/System/SystemStart.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/System/SystemStart.swift)** – Daemon lifecycle and system status queries.
- **[`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift)** – On-disk configuration persistence models.

## Summary

- The `ContainerAPIClient` module provides the **primary interface** for programmatic container management in `apple/container`.
- **XPCClient** and **XPCClientSession** handle transparent communication with the daemon via `Sources/ContainerXPC/`.
- All operations are **async/await-based**, returning specific Swift errors rather than exit codes.
- **Domain models** like `ContainerConfiguration` ensure type-safe parameter passing for container, image, network, and volume operations.
- You can perform complete lifecycle management—including pull, create, start, exec, list, and remove—without shelling out to command-line tools.

## Frequently Asked Questions

### How do I handle errors from the container daemon in the client API?

The client API propagates daemon-side failures as Swift `Error` types that you can catch using standard `do-catch` blocks. Each method is marked `throws` and returns detailed error information from the underlying XPC layer, allowing you to distinguish between network failures, image not found errors, and permission issues programmatically.

### Can I use the client API from a command-line tool or is it limited to GUI applications?

The `ContainerAPIClient` is available to any Swift process that can import the module, including command-line tools, daemons, and GUI applications. The XPC transport automatically manages the connection to the system-wide container daemon, regardless of the client process type, though sandboxed apps may require specific entitlements to communicate with the service.

### What is the difference between XPCClient and ContainerAPIClient?

`XPCClient` in [`Sources/ContainerXPC/XPCClient.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCClient.swift) provides the low-level transport mechanism for sending raw XPC messages, while `ContainerAPIClient` represents the high-level module containing type-safe wrappers. For programmatic container management, you typically use the high-level methods which internally instantiate and manage `XPCClient` sessions, rather than interacting with XPC directly.

### Is the client API thread-safe for concurrent container operations?

Yes, the client API supports concurrent operations through Swift's structured concurrency. You can launch multiple `Task` instances to pull images, start containers, and stream logs simultaneously. The underlying `XPCClientSession` manages request serialization and multiplexing, ensuring thread-safe access to the daemon from multiple concurrent execution contexts.