# How the apple/container CLI Communicates with Its API Server: gRPC over Unix Domain Sockets

> Discover how the apple/container CLI communicates with its API server using gRPC over Unix domain sockets. Learn about protobuf-encoded RPC calls and the ContainerAPIClient.

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

---

**The apple/container CLI communicates with its API server via gRPC over a local Unix domain socket, using the ContainerAPIClient library to send protobuf-encoded RPC calls to the container-apiserver process.**

The apple/container repository provides a container management system written in Swift, where the command-line interface and the API server operate as separate executables on the same host. Understanding how the apple/container CLI communicates with its API server reveals a clean architecture built on gRPC, Protocol Buffers, and HTTP/2 transport over Unix domain sockets.

## Architecture Overview

The system consists of two independent Swift executables: the CLI binary (`container`) and the API server (`container-apiserver`). The CLI imports the **ContainerAPIClient** library, which contains automatically-generated gRPC stubs for protobuf services like `ContainersService`, `ImagesService`, and `NetworkService`. 

Communication follows a pure request-response flow over **HTTP/2 gRPC** through a **Unix domain socket** (defaulting to `/var/run/container.sock`). The server binds to this socket on startup, and the CLI creates a client that connects to the same endpoint, enabling type-safe RPC calls without custom HTTP logic.

## The Communication Stack

### gRPC and Protocol Buffers

The interface is defined by protobuf files that generate Swift stubs for both client and server. In [`Sources/Services/ContainerAPIService/Client/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ContainersService.swift), the `ContainersServiceClient` class provides the client-side implementation generated from the protobuf definitions. This approach eliminates manual serialization and provides strongly-typed methods for container operations.

### Unix Domain Socket Transport

Rather than TCP/IP, the transport uses a local Unix socket for low-latency inter-process communication. The server binds to `/var/run/container.sock` (or a custom path specified via `--socket`), and the CLI connects to this endpoint. This keeps all traffic within the host kernel, avoiding network stack overhead while maintaining the HTTP/2 framing required by gRPC.

## Code Walkthrough: From CLI to Server

### Starting the API Server

The server initialization happens in `Sources/APIServer/APIServer+Start.swift`. The `APIServer.main()` function constructs a `GRPCServer` and binds it to the Unix socket path:

```swift
// Conceptual flow based on APIServer+Start.swift
let server = GRPCServer(group: group, services: [ContainersService()])
try await server.bind(unixDomainSocketPath: "/var/run/container.sock")
try await server.serve()

```

This creates the listening endpoint that accepts incoming gRPC connections from the CLI.

### CLI Entry Point and Command Routing

The CLI launch sequence begins in [`Sources/CLI/ContainerCLI.swift`](https://github.com/apple/container/blob/main/Sources/CLI/ContainerCLI.swift), where the `@main` struct instantiates an ArgumentParser-based `Application`. This application, defined in [`Sources/ContainerCommands/Application.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Application.swift), registers all sub-commands (such as `container start` or `container list`) and wires them to the appropriate client methods.

### Creating the gRPC Client

Each sub-command constructs a client using the `ContainerAPIClient` library. For example, [`Sources/ContainerCommands/Container/ContainerStart.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerStart.swift) implements the start command by creating a client and invoking the RPC:

```swift
import ContainerAPIClient

let client = try ContainerAPIClient.makeClient(socketPath: "/var/run/container.sock")
let request = ContainersService_StartRequest(containerID: id)
let response = try await client.containers.start(request)

```

The `makeClient` method abstracts the channel creation and stub initialization, returning a ready-to-use service client.

### Transport Layer Implementation

The underlying transport is implemented in [`Sources/ContainerAPIClient/GRPCChannel.swift`](https://github.com/apple/container/blob/main/Sources/ContainerAPIClient/GRPCChannel.swift). This file configures a `MultiThreadedEventLoopGroup` and initializes a `GRPCChannelPool` with the Unix domain socket path:

```swift
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let channel = try GRPCChannelPool.withUnixDomainSocket(path: socketPath, group: group)

```

The channel uses **GRPCSwift** (`GRPCNIOTransport`) running on **SwiftNIO** and **NIOCore** to manage the HTTP/2 connection over the Unix socket.

### Server-Side Request Handling

On the server side, [`Sources/Services/ContainerAPIService/Server/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/ContainersService.swift) implements the protobuf service interface. When the CLI sends an RPC, the server receives the protobuf message, executes the corresponding container operation, and returns a serialized response through the same gRPC channel.

## Auxiliary HTTP Communication

While gRPC handles the primary RPC flow, the CLI also performs direct HTTP requests for file transfers. The `ContainerAPIClient.FileDownloader` class (located in [`Sources/ContainerAPIClient/FileDownloader.swift`](https://github.com/apple/container/blob/main/Sources/ContainerAPIClient/FileDownloader.swift)) uses **AsyncHTTPClient** to download images, kernel tarballs, and other large files. These operations use standard HTTP GET/PUT requests independently of the gRPC channel, as defined in the [`Package.swift`](https://github.com/apple/container/blob/main/Package.swift) dependencies.

## Summary

- The **apple/container CLI** uses the **ContainerAPIClient** library to communicate with the API server via gRPC over a **Unix domain socket** at `/var/run/container.sock`.
- **Protocol Buffer** stubs in `Sources/Services/ContainerAPIService/Client/` provide type-safe client methods for container, image, and network operations.
- The transport layer leverages **SwiftNIO** and **GRPCSwift** (`GRPCNIOTransport`) for HTTP/2 communication through `GRPCChannelPool`.
- The server binds to the socket in `Sources/APIServer/APIServer+Start.swift` and implements services in [`Sources/Services/ContainerAPIService/Server/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/ContainersService.swift).
- **AsyncHTTPClient** handles auxiliary file downloads separately from the main gRPC control plane.

## Frequently Asked Questions

### What protocol does the apple/container CLI use to talk to the API server?

The CLI uses **gRPC over HTTP/2** transported via a **Unix domain socket**. This is implemented through the ContainerAPIClient library, which generates Swift stubs from Protocol Buffer definitions and manages the connection using SwiftNIO's `GRPCChannelPool`.

### Where is the Unix socket file located?

By default, the socket is created at `/var/run/container.sock`. You can specify a custom path using the `--socket` flag when starting the `container-apiserver` binary, and the CLI must reference the same path when calling `ContainerAPIClient.makeClient(socketPath:)`.

### Does the CLI use HTTP for all communication?

No. While the primary control plane uses gRPC, the CLI uses **AsyncHTTPClient** only for auxiliary file downloads (such as pulling container images or kernel tarballs) via the `FileDownloader` class in [`Sources/ContainerAPIClient/FileDownloader.swift`](https://github.com/apple/container/blob/main/Sources/ContainerAPIClient/FileDownloader.swift). These HTTP calls are separate from the gRPC channel used for container management operations.

### How are the client stubs generated?

The stubs are automatically generated from protobuf service definitions (such as `containers.proto`) and located in [`Sources/Services/ContainerAPIService/Client/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ContainersService.swift). The generated code provides the `ContainersServiceClient` class that the CLI uses to invoke remote procedures on the server.