How to Manage the Container Lifecycle with Apple's Container CLI: Create, Start, Stop, and Delete

The container command-line tool provides dedicated subcommands—create, start, stop, and delete—that orchestrate the complete lifecycle of container-based virtual machines on macOS, from initial metadata instantiation through resource cleanup.

The apple/container repository ships with a native macOS container runtime that leverages the Virtualization framework to run lightweight Linux VMs. Understanding how to manage the container lifecycle is essential for developers automating CI pipelines, scripting development environments, or running isolated workloads on Apple silicon.

Creating a Container

The container create Command

Use container create [<options>] <image> [<args> …] to instantiate a stopped container from an OCI image. This command only writes the container’s metadata and prepares the VM; the process does not run yet.

According to the source code in docs/command-reference.md, the CLI parses arguments and forwards the request to the container-runtime-linux XPC helper. Internally, this helper writes a ContainerSpec to the local content store managed by the container-apiserver service.


# Create a stopped container named "my-app" from Ubuntu

container create --name my-app ubuntu:latest

Starting a Container

The container start Command

The container start [--attach] [--interactive] [--debug] <container-id> command boots a previously created container. When --attach is provided, the runtime streams the VM’s console to your terminal.

As implemented in Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift, the start logic calls RuntimeClient.start(containerID:). The runtime loads the spec, creates the virtual machine via the macOS Virtualization framework, and begins execution of the init process.


# Start the container and attach to its console

container start --attach my-app

Stopping a Container

The container stop Command

To gracefully shut down a running VM, use container stop <container-id>. The implementation sends a SIGTERM to the init process, halts the VM instance via the Virtualization framework, and preserves the container metadata for later reuse.

The stop flow is defined in the same runtime client (RuntimeClient.stop(containerID:)) and communicates with the container-runtime-linux helper through XPC to ensure clean termination.


# Gracefully stop the container

container stop my-app

For automation scenarios, the repository includes scripts/ensure-container-stopped.sh, a helper utility used by CI pipelines to guarantee a clean environment before tests by forcibly ensuring containers reach a stopped state.

Deleting a Container

The container delete Command

The container delete <container-id> command removes all persisted state for the container, including metadata, root-fs snapshots, and attached volumes. After deletion, the container ID can be reused.

Deletion is performed by the ContainerAPIService (accessible via Sources/Services/ContainerAPIService/Client/XPC+.swift), which wipes the container directory under $HOME/.container/containers.


# Permanently remove the container and its resources

container delete my-app

Complete Lifecycle Workflow Examples

Bash Automation Script

For CI/CD pipelines, chain the lifecycle commands to ensure clean state:

#!/usr/bin/env bash
set -euo pipefail

# Clean up any existing container

container stop my-app || true
container delete my-app || true

# Create and start fresh

container create --name my-app ubuntu:latest
container start --attach my-app

Swift Programmatic API

You can also manage the lifecycle directly via Swift using the XPC client:

import ContainerAPIService

let client = ContainerAPIService.Client()

// 1. Create
try client.create(image: "ubuntu:latest", name: "my-app")

// 2. Start
try client.start(containerID: "my-app")

// ... perform work ...

// 3. Stop
try client.stop(containerID: "my-app")

// 4. Delete
try client.delete(containerID: "my-app")

The Swift client mirrors the CLI subcommands and ultimately communicates with the same XPC services (container-runtime-linux and container-apiserver) that the container binary uses.

Key Implementation Files

File Purpose
docs/command-reference.md Specification of container create, start, stop, and delete arguments
Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift Runtime constants driving the start and stop behavior via RuntimeClient
Sources/Services/ContainerAPIService/Client/XPC+.swift XPC client implementation forwarding delete requests to container-apiserver
scripts/ensure-container-stopped.sh CI helper ensuring containers are stopped before test runs
Tests/IntegrationTests/Utilities/ContainerFixture+ContainerHelpers.swift Test harness programmatically driving the lifecycle for verification

Summary

  • Creation (container create) writes a ContainerSpec via the XPC helper but leaves the VM in a stopped state.
  • Starting (container start) invokes RuntimeClient.start(containerID:) to boot the VM using Apple's Virtualization framework.
  • Stopping (container stop) sends SIGTERM to the init process and halts the VM while preserving metadata.
  • Deletion (container delete) triggers ContainerAPIService to remove all data from $HOME/.container/containers.
  • While container run combines creation and startup, explicit lifecycle management provides finer-grained control for automation and resource inspection.

Frequently Asked Questions

What is the difference between container run and container create?

container run combines creation and immediate startup into a single command, whereas container create only instantiates the metadata and prepares the VM without booting it. Using create → start allows you to inspect container state, modify resources, or inject custom init scripts before execution begins.

Where does the container runtime store persistent data?

The runtime stores container metadata and root-fs snapshots in the $HOME/.container/containers directory, as managed by the container-apiserver service. When you invoke container delete, the ContainerAPIService wipes this directory for the specific container ID.

How does the container stop command ensure graceful shutdown?

The stop command sends a SIGTERM signal to the container's init process and instructs the Virtualization framework to halt the VM. This flow is implemented in RuntimeClient.stop(containerID:) and communicates via XPC to the container-runtime-linux helper, ensuring the VM terminates cleanly while preserving the container's configuration for potential restarts.

Can I manage containers programmatically instead of using the CLI?

Yes. The ContainerAPIService.Client() Swift class exposed in Sources/Services/ContainerAPIService/Client/XPC+.swift provides create(), start(), stop(), and delete() methods that mirror the CLI. These methods communicate with the same XPC services, making them suitable for macOS applications that need to embed container lifecycle management.

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 →