# How Containers Are Created in Apple Container: CLI to Runtime Architecture

> Discover how Apple Container creates containers using its CLI to runtime architecture. Understand the three-step pipeline from argument parsing to sandbox instantiation.

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

---

**Apple Container creates containers through a three-step pipeline: the CLI parses arguments into a `ContainerConfiguration`, a `ContainerClient` sends an XPC message to the daemon, and the server instantiates a sandbox via the OCI runtime.**

The Apple Container repository provides a Swift-based container runtime for macOS that bridges user commands with low-level kernel virtualization. Understanding how containers are created in Apple Container reveals a tightly coupled architecture spanning the command line, XPC inter-process communication, and OCI-compliant bundle construction.

## The Three-Stage Container Creation Pipeline

### Step 1: CLI Parsing and Configuration Assembly

In [`Sources/ContainerCommands/Container/ContainerCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerCreate.swift), the `Application.ContainerCreate` subcommand handles user input. The `run()` method gathers flags and invokes `Utility.containerConfigFromFlags()` (defined in [`Sources/ContainerCommands/Container/Utility.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/Utility.swift)) to build a complete `ContainerConfiguration`, kernel specification, and `ContainerCreateOptions` struct.

### Step 2: XPC Transmission to the API Server

The CLI creates a `ContainerClient` instance ([`Sources/Services/ContainerAPIService/Client/ContainerClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ContainerClient.swift)), which acts as a thin XPC wrapper. It constructs an `XPCMessage` with the route `containerCreate` (defined in [`Sources/ContainerXPC/XPCMessage.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCMessage.swift)), encoding the configuration, kernel, and options as JSON payloads before sending the message to `com.apple.container.apiserver`.

### Step 3: Runtime Instantiation and Bundle Creation

The daemon receives the request in [`Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift), decodes the payload, and delegates to the OCI runtime. It uses [`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift) to materialize the bundle, create the rootfs, configure cgroups, mount filesystems, and register the container with a generated ID.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`Sources/ContainerCommands/Container/ContainerCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerCreate.swift) | CLI entry point that validates arguments and triggers creation |
| [`Sources/ContainerCommands/Container/Utility.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/Utility.swift) | Helper functions translating flags into `ContainerConfiguration` |
| [`Sources/Services/ContainerAPIService/Client/ContainerClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ContainerClient.swift) | XPC client wrapper for daemon communication |
| [`Sources/ContainerXPC/XPCMessage.swift`](https://github.com/apple/container/blob/main/Sources/ContainerXPC/XPCMessage.swift) | Wire format and routing enum definitions |
| [`Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift) | Server-side handler for `containerCreate` requests |
| [`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift) | OCI bundle materialization and rootfs setup |

## Creating Containers via CLI and Swift API

### Command Line Interface

The simplest way to create a container uses the `container create` command:

```bash

# Create a container from the "alpine:3.22" image, naming it "my-alpine"

container create alpine:3.22 --name my-alpine

```

Behind the scenes, `Application.ContainerCreate.run()` executes:

```swift
let client = ContainerClient()
try await client.create(configuration: cfg, options: opts, kernel: kernel, initImage: initImg)
print(id) // prints the generated container ID

```

### Programmatic Creation

For direct integration, use the `ContainerClient` API:

```swift
import ContainerAPIClient
import ContainerResource

let config = try ContainerConfiguration(
    id: "demo-container",
    image: "docker.io/library/alpine:latest",
    command: ["/bin/sh"]
)

let options = ContainerCreateOptions(autoRemove: false)
let kernel = Kernel()
let client = ContainerClient()

Task {
    do {
        try await client.create(
            configuration: config,
            options: options,
            kernel: kernel,
            initImage: nil
        )
        print("Container created")
    } catch {
        print("❗️ failed: \(error)")
    }
}

```

The `client.create()` method internally constructs the XPC message:

```swift
let request = XPCMessage(route: .containerCreate)
request.set(key: .containerConfig, value: try JSONEncoder().encode(configuration))
request.set(key: .kernel, value: try JSONEncoder().encode(kernel))
request.set(key: .containerOptions, value: try JSONEncoder().encode(options))
try await xpcSend(message: request)

```

### Verifying Container Creation

Inspect the created container using the client:

```swift
let client = ContainerClient()
let snapshot = try await client.get(id: "demo-container")
print("State:", snapshot.status.state) // e.g. "running"

```

## Summary

- Apple Container creates containers through a three-step pipeline: CLI configuration, XPC transmission, and runtime instantiation.
- The [`ContainerCreate.swift`](https://github.com/apple/container/blob/main/ContainerCreate.swift) CLI command builds configurations via [`Utility.swift`](https://github.com/apple/container/blob/main/Utility.swift) before invoking `ContainerClient.create()`.
- XPC messages use the `containerCreate` route to communicate with the privileged daemon at `com.apple.container.apiserver`.
- The server implementation in [`ContainersService.swift`](https://github.com/apple/container/blob/main/ContainersService.swift) delegates to [`Builder.swift`](https://github.com/apple/container/blob/main/Builder.swift) for OCI bundle materialization and rootfs setup.
- Both CLI and programmatic APIs generate a unique container ID and return it to the caller upon successful creation.

## Frequently Asked Questions

### What is the role of XPC in Apple Container creation?

XPC provides the secure inter-process communication layer between the unprivileged CLI client and the privileged Container-API-Server daemon. The `ContainerClient` encodes the `ContainerConfiguration` as JSON and sends it via `XPCMessage` with the `containerCreate` route, ensuring proper sandboxing and privilege separation during container instantiation.

### How does Apple Container handle OCI bundle creation?

The server-side [`ContainersService.swift`](https://github.com/apple/container/blob/main/ContainersService.swift) receives the creation request and delegates bundle materialization to [`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift). This component writes the rootfs, generates the [`config.json`](https://github.com/apple/container/blob/main/config.json) specification, and sets up the required filesystem mounts and cgroups before handing execution to the underlying OCI runtime.

### Can I create containers programmatically without using the CLI?

Yes. The `ContainerClient` class in [`Sources/Services/ContainerAPIService/Client/ContainerClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ContainerClient.swift) exposes a Swift API that accepts `ContainerConfiguration`, `ContainerCreateOptions`, and `Kernel` objects. Calling `client.create()` constructs the appropriate XPC message and communicates directly with the daemon, bypassing the command-line interface entirely.

### Where is the container configuration validated before runtime?

Initial validation occurs in [`Sources/ContainerCommands/Container/Utility.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/Utility.swift) through `containerConfigFromFlags()`, which translates CLI arguments into strongly-typed structures. Additional server-side validation occurs in [`ContainersService.swift`](https://github.com/apple/container/blob/main/ContainersService.swift) when the daemon decodes the XPC payload and verifies the configuration before invoking the OCI runtime to prevent invalid container states.