# How Apple Container Manages Storage: Filesystem Persistence Architecture

> Discover how Apple Container manages storage using a filesystem persistence layer. Learn how volumes, image snapshots, and metadata are stored and managed on macOS.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: architecture
- Published: 2026-06-27

---

**Apple Container uses a file‑system‑backed persistence layer where volumes, image snapshots, and metadata are stored as regular files and JSON documents on macOS, managed by `VolumesService`, `SnapshotStore`, and `FilesystemEntityStore`.**

The `apple/container` repository provides a macOS-native container runtime that avoids complex storage drivers by leveraging the host file system for all persistence. Understanding how apple container manages storage reveals a synchronous, file-based architecture where virtual disks appear as ordinary files that standard macOS tools can inspect and manage.

## Core Storage Components

Apple Container organizes disk state into four distinct categories, each handled by dedicated services in the codebase.

### Volumes and Block Storage

User-defined data volumes are stored as block device images accompanied by JSON configuration files. According to the source code, `VolumesService` (in [`Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift)) creates a directory structure under `resourceRoot/volumes/<volume-name>/` containing:

- `volume.img`: The actual block file representing the virtual disk
- [`entity.json`](https://github.com/apple/container/blob/main/entity.json): Metadata describing the volume name, driver, labels, and size quota

Disk usage is calculated using `FileManager.default.allocatedSize(of:)` on the block file, allowing the system to report precise consumption through `volumeDiskUsage(name:)` and aggregate totals via `calculateDiskUsage()`.

### Image Snapshots

Unpacked OCI image layers reside as EXT4 file systems in the `snapshots/<digest>/` directory. The `SnapshotStore` (in [`Sources/Services/ContainerImagesService/Server/SnapshotStore.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/SnapshotStore.swift)) manages these by:

- Writing `snapshot` (the block file) and `snapshot-info` (JSON description) after unpacking
- Maintaining an "ingest" staging area for atomic moves
- Running `clean(keepingSnapshotsFor:)` to remove unreferenced snapshots and return reclaimed byte counts

### Metadata Persistence

All entity descriptions are persisted via `FilesystemEntityStore` (in [`Sources/ContainerPersistence/EntityStore.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/EntityStore.swift)). This store:

- Loads all [`entity.json`](https://github.com/apple/container/blob/main/entity.json) files at startup into an in-memory index
- Provides synchronous CRUD operations used by both `VolumesService` and `NetworksService`
- Ensures consistency between the file system state and the runtime's view of resources

### Storage Quotas

Per-container limits are declared in [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift) ([`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift)) via the `storage: UInt64?` field. When starting a container, the runtime validates the requested size against a minimum of 1 MiB, raising `VolumeError.storageError` for invalid configurations.

## Volume Lifecycle Operations

Creating, inspecting, and deleting volumes follows a predictable file-based workflow.

### Creating a Volume

When you run `container volume create mydata`, the CLI invokes `VolumesService.create`, which locks the resource and writes the block file and metadata:

```swift
// In VolumesService.swift
public func create(name: String, driver: String = "local", …) async throws -> VolumeConfiguration {
    return try await lock.withLock { _ in
        try await self._create(name: name, driver: driver, …)
    }
}

```

The method atomically creates `volume.img` and [`entity.json`](https://github.com/apple/container/blob/main/entity.json) through the `FilesystemEntityStore`.

### Inspecting Disk Usage

To check a volume's size or system-wide storage:

```bash
container volume inspect mydata
container system df      # shows total and reclaimable storage

```

The underlying implementation uses `FileManager.default.allocatedSize`:

```swift
let volumePath = self.volumePath(for: name)
return FileManager.default.allocatedSize(of: URL(fileURLWithPath: volumePath))

```

### Deleting Volumes

`VolumesService.delete` removes both the block file and its corresponding [`entity.json`](https://github.com/apple/container/blob/main/entity.json), while `FilesystemEntityStore` drops the entry from its in-memory index, immediately reclaiming disk space.

## Image Snapshot Management

The snapshot system handles unpacked container layers with an emphasis on atomic operations and garbage collection.

### Unpacking Layers

`SnapshotStore.unpack` extracts image layers into a temporary directory before atomically moving them to `snapshots/<digest>/`. This prevents partial writes from corrupting the storage.

### Reclaiming Space

To clean up unused snapshots programmatically:

```swift
let reclaimed = try await snapshotStore.clean(keepingSnapshotsFor: [image])
print("Reclaimed \(reclaimed) bytes")

```

This method scans the `snapshots/` directory, preserves only digests referenced by retained images, and returns the total bytes freed.

## Storage Quota Enforcement

Container-level storage limits are enforced at startup. When using the CLI:

```bash
container run --storage 10g myimage

```

The flag populates the `storage` field in `ContainerConfiguration`. The runtime validates this value against the 1 MiB minimum, throwing `VolumeError.storageError` if the quota is too small.

## Summary

- **Apple Container stores all state as regular files** on the macOS file system, including block images for volumes and unpacked layers for images.
- **VolumesService** manages volume lifecycle in [`Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift), using `FileManager.default.allocatedSize(of:)` for usage tracking.
- **SnapshotStore** handles image layers in [`Sources/Services/ContainerImagesService/Server/SnapshotStore.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/SnapshotStore.swift), with atomic unpack and reference-based cleanup.
- **FilesystemEntityStore** provides JSON metadata persistence in [`Sources/ContainerPersistence/EntityStore.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/EntityStore.swift), maintaining an in-memory index of all entities.
- **Storage quotas** are enforced via [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift) with a minimum valid size of 1 MiB.

## Frequently Asked Questions

### How does Apple Container track disk usage for volumes?

Apple Container tracks disk usage by calling `FileManager.default.allocatedSize(of:)` on the `volume.img` block file. The `VolumesService.volumeDiskUsage(name:)` method returns this value, while `calculateDiskUsage()` aggregates sizes across all volumes to report total, active, and reclaimable storage counts.

### Where are container image layers stored in Apple Container?

Image layers are stored as unpacked EXT4 file systems in `snapshots/<digest>/`, containing a `snapshot` block file and `snapshot-info` JSON metadata. The `SnapshotStore` in [`Sources/Services/ContainerImagesService/Server/SnapshotStore.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/SnapshotStore.swift) manages these directories, using an "ingest" staging area for atomic writes.

### What happens when a volume is deleted in Apple Container?

When `VolumesService.delete` is invoked, it removes both the `volume.img` block file and the [`entity.json`](https://github.com/apple/container/blob/main/entity.json) metadata from `resourceRoot/volumes/<volume-name>/`. The `FilesystemEntityStore` automatically removes the entry from its in-memory index, synchronously reclaiming the disk space on the host file system.

### How are storage quotas enforced for individual containers?

Storage quotas are declared in the `storage: UInt64?` field of `ContainerConfiguration` (defined in [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift)). When a container starts, the runtime validates the requested size against a minimum of 1 MiB and raises `VolumeError.storageError` if the configuration is invalid.