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

> Master container lifecycle management with Apple's Container CLI. Learn to create, start, stop, and delete containers efficiently on macOS and free up resources.

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

---

**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`](https://github.com/apple/container/blob/main/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.

```bash

# 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`](https://github.com/apple/container/blob/main/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.

```bash

# 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.

```bash

# Gracefully stop the container

container stop my-app

```

For automation scenarios, the repository includes [`scripts/ensure-container-stopped.sh`](https://github.com/apple/container/blob/main/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`.

```bash

# 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:

```bash
#!/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:

```swift
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`](https://github.com/apple/container/blob/main/docs/command-reference.md) | Specification of `container create`, `start`, `stop`, and `delete` arguments |
| [`Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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.