How the Apple Container Project Structures Its CLI Commands: A Deep Dive

The apple/container project organizes its CLI into a hierarchical tree of Swift structs conforming to AsyncLoggableCommand, anchored by a root Application command that groups subcommands into Container, Image, Machine, Volume, and Other categories.

The container command-line tool is Apple's open-source container platform for macOS, built on top of Swift ArgumentParser. Understanding the structure of commands in the apple/container project reveals how the tool manages containers, images, and virtual machines through a type-safe, declarative interface. Each command is implemented as a Swift struct that declares its behavior via static configuration properties, creating a discoverable command tree that scales from simple container creation to complex system management.

Root Command Architecture

The entire command hierarchy is anchored in Application, the root command representing the container binary itself. Located in Sources/ContainerCommands/Application.swift (lines 45-53), this struct conforms to AsyncLoggableCommand and declares a static configuration property of type CommandConfiguration.

This configuration defines the command name, abstract, version information, and the complete subcommand tree:

public static let configuration = CommandConfiguration(
    commandName: "container",
    abstract: "A container platform for macOS",
    version: ReleaseVersion.singleLine(appName: "container CLI"),
    subcommands: [
        DefaultCommand.self,
        HelpCommand.self,
    ],
    groupedSubcommands: [
        CommandGroup(name: "Container", subcommands: [  ]),
        CommandGroup(name: "Image",     subcommands: [  ]),
        CommandGroup(name: "Machine",   subcommands: [  ]),
        CommandGroup(name: "Volume",    subcommands: [  ]),
        CommandGroup(name: "Other",     subcommands: Self.otherCommands())
    ],
    defaultSubcommand: DefaultCommand.self
)

The root configuration includes two special top-level items: DefaultCommand (which handles unknown input for plugin loading) and HelpCommand (which implements container help …).

Command Group Organization

The apple/container project organizes functionality into five distinct command groups. Each group aggregates related subcommands, making the CLI discoverable and consistent with container ecosystem conventions.

Container Group

The Container group houses commands that operate on container lifecycles, including create, start, stop, exec, and prune operations. Each subcommand lives in Sources/ContainerCommands/Container/ and follows a consistent pattern.

For example, ContainerCreate (defined in ContainerCreate.swift, lines 30-33) declares its interface as:

public struct ContainerCreate: AsyncLoggableCommand {
    public static let configuration = CommandConfiguration(
        commandName: "create",
        abstract: "Create a new container"
    )
    // ...
}

Other container commands such as ContainerStart, ContainerStop, ContainerRun, and ContainerLogs follow this identical structure in the same directory, each implementing a run() method that performs the requested action via ContainerAPIClient.

Image Group

The Image group handles image-related operations including build, pull, push, tag, and prune. Rather than registering subcommands directly with the root, this group uses an entry point called ImageCommand, which aggregates its subcommands in Sources/ContainerCommands/Image/ImageCommand.swift (lines 34-40).

This pattern allows the image commands to share common flags and validation logic before dispatching to specific implementations like ImageBuild or ImagePull.

Machine Group

The Machine group manages the underlying virtual machine that powers the container runtime on macOS. Commands like MachineRun, MachineStart, and MachineStop reside in Sources/ContainerCommands/Machine/ and control the VM lifecycle independently of individual containers.

Volume Group

The Volume group provides volume lifecycle management through commands like VolumeCreate, VolumeList, VolumeInspect, and VolumeDelete. These implementations are located in Sources/ContainerCommands/Volume/ and follow the same AsyncLoggableCommand pattern as container commands.

Other Group

The Other group contains platform-specific commands that vary by macOS version. The otherCommands() method in Application.swift (lines 223-236) dynamically returns:

  • BuilderCommand – Builder lifecycle management
  • NetworkCommand – Network subcommands (list, create, inspect), available only on macOS 26+
  • SystemCommand – System daemon control (container system start|stop|status)

This conditional grouping ensures that network commands only appear on supported platforms while maintaining a consistent CLI structure across versions.

Command Resolution and Help System

When the binary executes, Swift ArgumentParser builds a command tree from these static configurations. The HelpCommand (located in Sources/ContainerCommands/HelpCommand.swift) resolves paths like container help image pull by walking the tree using resolveSubcommand (lines 44-52).

This resolution mechanism allows the help system to locate any subcommand regardless of nesting depth, printing the appropriate help text for the target command. The tree structure ensures that command paths follow logical groupings: container container create, container image pull, or container system start.

Summary

  • The root Application command in Sources/ContainerCommands/Application.swift defines the entire CLI hierarchy using CommandConfiguration.
  • Commands are organized into five groups: Container, Image, Machine, Volume, and Other.
  • Each command is a Swift struct conforming to AsyncLoggableCommand with a static configuration property declaring its name and abstract.
  • The Other group dynamically includes platform-specific commands (Network, System, Builder) based on the host macOS version.
  • HelpCommand uses resolveSubcommand to traverse the command tree and display contextual help for any nested subcommand.

Frequently Asked Questions

How does the container CLI handle unknown commands?

The Application configuration specifies DefaultCommand as the defaultSubcommand. This command catches unknown input and attempts plugin loading, allowing the CLI to be extended without modifying the core command tree.

Why are some commands missing on older macOS versions?

The otherCommands() method in Application.swift conditionally excludes NetworkCommand on macOS versions prior to 26. This dynamic grouping ensures that platform-specific features only appear when the underlying system supports them.

Where is the actual command execution logic implemented?

While the command structure and argument parsing are defined in Sources/ContainerCommands/, each leaf command's run() method typically delegates to a client from ContainerAPIClient. This separation keeps the CLI layer focused on argument parsing and validation while the API client handles the actual container operations.

How can I add a new subcommand to the container CLI?

Create a new Swift struct conforming to AsyncLoggableCommand in the appropriate group directory (e.g., Sources/ContainerCommands/Container/), define its CommandConfiguration with a unique commandName, and add it to the corresponding CommandGroup array in Application.swift. The ArgumentParser framework automatically integrates it into the help system and command tree.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →