How to Clean Up All Stopped Containers in Apple Container

The container prune command removes every stopped container by filtering for status .stopped, calculating disk usage, and deleting each container through the XPC client.

The Apple Container framework provides a dedicated CLI subcommand and Swift API to clean up all stopped containers. Whether you are scripting maintenance tasks or building a management interface, understanding the underlying implementation in ContainerPrune.swift helps you reclaim disk space safely and efficiently.

How the Prune Operation Works

The cleanup logic orchestrates three distinct phases to ensure accurate accounting and safe removal.

Filter Stopped Containers

The process begins in ContainerPrune.swift by constructing a ContainerListFilters value with status: .stopped. The implementation calls withoutMachines() to exclude machine containers from the deletion scope, ensuring only user-created stopped containers are targeted. This filter model is defined in Sources/ContainerResource/Container/ContainerListFilters.swift.

Calculate Reclaimed Space

For each container returned by the filtered list, the command gathers usage data. It invokes ContainerClient.diskUsage(id:) to compute the total bytes that will be freed. This step occurs in the client implementation at Sources/Services/ContainerAPIService/Client/ContainerClient.swift, which communicates with the container daemon over XPC.

Execute Deletion

Finally, the prune loop calls ContainerClient.delete(id:) for every stopped container. The accumulated disk usage totals are logged to provide immediate feedback on reclaimed storage. All XPC RPCs are handled asynchronously through the ContainerClient interface.

Key Source Files and Architecture

Understanding the file structure clarifies how the components interact:

Practical Code Examples

Using the CLI

The simplest way to clean up all stopped containers is via the command line:

container prune

This command automatically filters for stopped containers, calculates reclaimed space, and prints the IDs of deleted containers.

Swift Implementation

To implement the same logic programmatically:

import ContainerAPIClient
import ContainerResource

let client = ContainerClient()
let stoppedFilters = ContainerListFilters(status: .stopped).withoutMachines()
let stopped = try await client.list(filters: stoppedFilters)

var reclaimed: UInt64 = 0
for c in stopped {
    let size = try await client.diskUsage(id: c.id)
    reclaimed += size
    try await client.delete(id: c.id)
}
print("Removed \(stopped.count) stopped containers, reclaimed \(reclaimed) bytes")

Constructing Filters

When building custom management tools, you can inspect the filter construction:

let filters = ContainerListFilters(status: .stopped).withoutMachines()
// The filter encodes as JSON for transmission to the daemon
print(filters)

Summary

  • The container prune command targets only containers with status .stopped while excluding machine containers via withoutMachines().
  • The operation relies on ContainerClient XPC methods list, diskUsage, and delete to communicate with the container daemon.
  • All logic is implemented in ContainerPrune.swift, with supporting filter models in ContainerListFilters.swift.
  • Both CLI and Swift APIs provide accurate byte-level reporting of reclaimed disk space.

Frequently Asked Questions

What is the difference between container prune and manual deletion?

Manual deletion requires you to specify individual container IDs and does not automatically calculate reclaimed space. The container prune command aggregates all stopped containers, computes total disk usage via diskUsage(id:), and removes them in a single operation with detailed logging.

Why does the prune command exclude machine containers?

The withoutMachines() filter removes infrastructure containers that support the Apple Container runtime itself. According to the source in ContainerListFilters.swift, machine containers are system-level resources that should persist regardless of user cleanup operations to maintain daemon functionality.

How does the XPC client communicate with the container daemon?

The ContainerClient defined in ContainerClient.swift establishes an XPC connection to the container daemon. When you call list, diskUsage, or delete, the client serializes requests and sends them as RPCs, awaiting asynchronous responses before updating the local process with container state or confirmation of deletion.

Is it safe to run container prune on a production system?

Yes, provided you verify that no stopped containers contain data you wish to preserve. The command only affects containers with status .stopped and leaves running containers untouched. However, the deletion via delete(id:) is irreversible, so ensure critical stopped containers are backed up or committed to images before pruning.

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 →