How to Create and Manage Persistent Storage Volumes for Containers in Apple Container

The Apple Container project provides a built-in volume subsystem that lets you create, list, inspect, and delete persistent storage volumes that survive container restarts and can be shared across multiple containers.

The apple/container repository implements a complete volume management API that handles persistent storage independently of container images. These volumes live on the host filesystem and are managed by the ContainerAPIClient, ensuring data persists beyond individual container lifecycles. Understanding how to create and manage persistent storage volumes for containers is essential for stateful workloads and data sharing between container instances.

Understanding Volume Types and Concepts

The Container project distinguishes between several volume types, each with specific use cases and validation rules.

Named Volumes

Named volumes are explicitly created using container volume create <name> or implicitly when using the -v <name>:/path flag. Names must match the VolumeResource.volumeNamePattern regex (^[A-Za-z0-9][A-Za-z0-9_.-]*$) and are capped at 255 characters according to the validation logic in Sources/ContainerResource/Volume/VolumeResource.swift.

Anonymous Volumes

Anonymous volumes are auto-generated when you omit the host path in mount flags (using -v /path or --mount type=volume,dst=/path). The system assigns a UUID-based name following the pattern anon-<36-char-uuid> and marks it via the VolumeConfiguration.isAnonymous flag derived from special labels added by the daemon.

Driver-Specific Options

The default local driver supports custom options passed via --opt key=value. Key options include:

  • size: Specifies volume capacity (e.g., 10g)
  • journal: Configures ext4 journaling mode (ordered, writeback, or writeback:64m for custom journal size)

Labels and Metadata

Arbitrary key/value metadata can be attached using --label <key>=<value>. These labels are validated by ResourceLabels and stored in VolumeConfiguration.labels as implemented in VolumeResource.swift.

Volume Lifecycle Rules

The Container project enforces strict rules to prevent data loss:

  • Attachment blocking: A volume cannot be deleted while attached to any container, whether running or stopped.
  • Anonymous persistence: Unlike Docker behavior, anonymous volumes are not automatically removed when using --rm on containers. They must be deleted manually via container volume delete.
  • Multi-container sharing: Volumes can be attached to multiple containers simultaneously, enabling shared state between instances.

Architecture Overview

The volume subsystem follows a client-server architecture with clear separation between API and resource representation.

VolumeResource

VolumeResource (defined in Sources/ContainerResource/Volume/VolumeResource.swift) is a lightweight wrapper conforming to ManagedResource. It exposes:

  • Volume ID and name
  • Creation timestamp
  • Label metadata
  • Anonymity flag (isAnonymous)

This struct enables generic CLI handling across the framework while wrapping the underlying VolumeConfiguration objects.

ClientVolume and API Flow

The ClientVolume client (part of ContainerAPIClient) handles RPC communication with the daemon. Key methods include:

  • list(): Fetches all volume configurations from the daemon
  • create(): Sends volume creation RPCs with driver options
  • delete(): Removes volumes after verifying no attachments exist

CLI commands in Application.VolumeCommand parse user flags, invoke these ClientVolume methods, translate results into VolumeResource instances, and render output via the generic Output helper (as seen in Sources/ContainerCommands/Volume/VolumeList.swift and Sources/ContainerCommands/Volume/VolumeInspect.swift).

Creating Persistent Storage Volumes

Create named volumes with specific capacity and journaling modes using the container volume create command.

Create a 10 GiB volume with ordered journaling:

container volume create -s 10g --opt journal=ordered mydata

Create a volume with writeback journaling and custom journal size:

container volume create --opt journal=writeback:64m myfastvol

Create a volume with custom labels:

container volume create --label environment=production --label tier=database pgdata

Listing and Inspecting Volumes

View all volumes in table format or extract specific names for scripting.

List all volumes with details:

container volume list

List only volume names (quiet mode):

container volume list -q

Inspect a specific volume's metadata, driver options, and usage:

container volume inspect mydata

Attaching Volumes to Containers

Mount existing volumes or create anonymous volumes during container runtime.

Attach a named volume to a container:

container run -v mydata:/app/data alpine sh

Use the --mount syntax for explicit volume type declaration:

container run --mount type=volume,src=mydata,dst=/app/data alpine sh

Create and mount an anonymous volume (auto-generates UUID-based name):

container run -v /tmp/cache alpine sh

Reuse an existing anonymous volume by referencing its generated name:

VOL=$(container volume list -q | grep anon)
container run -v $VOL:/app/cache alpine sh

Deleting Volumes

Remove volumes that are no longer in use, with safety checks for attached containers.

Delete a specific volume (fails if still attached):

container volume delete mydata

Delete all volumes (use with caution):

container volume delete --all

Clean up anonymous volumes manually (since they persist after container removal):

container volume list -q | grep anon | xargs container volume delete

Summary

  • Named volumes require alphanumeric names matching ^[A-Za-z0-9][A-Za-z0-9_.-]*$ and support driver options for size and journaling.
  • Anonymous volumes auto-generate UUID-based names (anon-<uuid>) and persist after container removal unless manually deleted.
  • Lifecycle protection prevents deletion of attached volumes, while the VolumeResource struct in VolumeResource.swift wraps daemon configurations for CLI presentation.
  • Driver options for the local driver include size capacity limits and journal mode configuration for ext4 filesystems.
  • The ClientVolume API in ContainerAPIClient handles create, list, inspect, and delete operations, with CLI implementations residing in VolumeList.swift and VolumeInspect.swift.

Frequently Asked Questions

What's the difference between named and anonymous volumes?

Named volumes are explicitly created with container volume create or referenced by name in mount flags, persist until manually deleted, and support custom driver options. Anonymous volumes are auto-generated when using -v /path without a host name, receive a UUID-based name (anon-<36-char-uuid>), and are marked via the isAnonymous flag in VolumeConfiguration.

Can I delete a volume while it's attached to a container?

No. The Container project enforces attachment validation before deletion. The container volume delete command will fail if the volume is mounted by any container, regardless of whether that container is running or stopped. You must stop and remove all referencing containers before the volume can be deleted.

How do I configure volume size and journaling options?

Pass driver-specific options using --opt flags during creation. The default local driver accepts size (e.g., -s 10g or --opt size=10g) and journal settings (--opt journal=ordered or --opt journal=writeback:64m). These options configure the underlying ext4 filesystem during volume initialization.

Do anonymous volumes get automatically removed when the container stops?

No. Unlike some container runtimes, anonymous volumes in the Apple Container project do not get removed when using --rm on containers. They persist on the host filesystem and require manual deletion via container volume delete <anon-uuid> or container volume delete --all when no longer needed.

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 →