# How Apple’s Container Runtime Handles Persistent Storage for Containers

> Discover how Apple's container runtime uses a filesystem-backed entity store to handle persistent storage for containers, serializing objects as JSON.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: internals
- Published: 2026-07-01

---

**Apple’s container runtime persists container state using a filesystem-backed entity store that serializes each object as JSON in type-specific directories under `/var/run/container/`.**

Apple’s open-source container runtime (`apple/container`) manages persistent storage for containers through a structured entity store system. Rather than relying on external databases, the runtime serializes container metadata, volumes, and network configurations as JSON files directly on the host filesystem. This approach ensures state survives process restarts while maintaining simplicity and portability across macOS and Linux systems.

## Entity Store Architecture

The runtime abstracts persistence through a generic protocol that defines how container-related objects are saved, retrieved, and updated.

### The EntityStore Protocol

In [`Sources/ContainerPersistence/EntityStore.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/EntityStore.swift), every persistent object implements the `EntityStore<T>` protocol. This protocol defines standard CRUD operations including `list`, `create`, `retrieve`, `update`, `upsert`, and `delete`. The generic constraint `T: Codable & Identifiable<String>` guarantees that each entity can be encoded to JSON and addressed by a unique string identifier.

### FilesystemEntityStore Implementation

The concrete implementation `FilesystemEntityStore<T>` receives a `FilePath` pointing to a dedicated directory for the entity type (e.g., *containers*, *volumes*, or *networks*). For each entity, the store creates a subdirectory named after the entity’s ID and writes a file called [`entity.json`](https://github.com/apple/container/blob/main/entity.json) containing the JSON representation of the entire object.

## Filesystem Layout and JSON Serialization

The runtime uses a predictable directory structure to isolate persistent data and ensure atomic operations.

### Directory Structure and Metadata Files

Each entity type stores its data under `/var/run/container/<type>` on macOS and Linux systems. The constant `metadataFilename = "entity.json"` serves as the standard name for serialized records. When creating a new entity, the store first verifies that [`entity.json`](https://github.com/apple/container/blob/main/entity.json) does not already exist to prevent accidental overwrites, then writes the payload using `JSONEncoder`. Updates rewrite the same file atomically.

### Startup Loading and Decoding

On startup, the store scans the type-specific directory, reads every [`entity.json`](https://github.com/apple/container/blob/main/entity.json) file, and decodes it with `JSONDecoder` to build an in-memory index (`[String: T]`). Failures to decode individual files are logged as warnings but do not abort the load process, ensuring that corrupted data does not bring down the entire runtime.

## Volume Configuration and Resource Management

Volumes demonstrate how the runtime distinguishes between temporary and long-lived storage.

### Anonymous vs. Persistent Volumes

In [`Sources/ContainerResource/Volume/VolumeResource.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Volume/VolumeResource.swift), the `VolumeConfiguration` struct holds metadata including name, labels, creation date, and an `isAnonymous` flag. Anonymous volumes are marked as ephemeral and are automatically deleted when the owning container stops. Persistent volumes lack this flag, allowing their [`entity.json`](https://github.com/apple/container/blob/main/entity.json) files to remain on disk across container lifetimes.

### VolumeResource and VolumeConfiguration

The `VolumeResource` type wraps `VolumeConfiguration` and conforms to `ManagedResource`, exposing the ID, name, creation date, and labels. Because the configuration is `Codable`, it persists without special handling. The `VolumeResource.isAnonymous` property enforces the distinction between ephemeral and persistent storage at runtime.

## System Configuration and Initialization

The top-level `ContainerSystemConfig` (defined in [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift)) aggregates individual stores for containers, volumes, networks, and other resources. When the runtime initializes, it constructs each `FilesystemEntityStore` with a path rooted under the system’s data directory. This hierarchy mirrors the logical organization of entities and guarantees isolated persistent storage areas for each resource type.

## Error Handling and Safety Guarantees

All filesystem operations execute through `FileManager`, with failures raising `ContainerizationError` containing clear diagnostic messages. If the store encounters an unreadable [`entity.json`](https://github.com/apple/container/blob/main/entity.json) during startup, it logs the specific file path and corruption details while continuing to load valid entities. This defensive approach prevents stale or corrupted data from interrupting service availability.

## Working Example: Persisting Volume Configuration

The following Swift code demonstrates how to create and retrieve persistent volume metadata using the runtime’s entity store:

```swift
import Foundation
import Logging
import SystemPackage
import ContainerPersistence

// 1️⃣ Create a logger
var logger = Logger(label: "container.runtime")

// 2️⃣ Define a directory for persistent volume metadata
let volumesPath = FilePath("/var/run/container/volumes")

// 3️⃣ Initialise a FilesystemEntityStore for VolumeConfiguration
let volumeStore = try FilesystemEntityStore<VolumeConfiguration>(
    path: volumesPath,
    type: "volume",
    log: logger)

// 4️⃣ Define a new persistent volume
let newVolume = VolumeConfiguration(
    name: "my-data",
    creationDate: Date(),
    labels: ["app": "web"],
    isAnonymous: false)

// 5️⃣ Persist the volume
try await volumeStore.create(newVolume)

// 6️⃣ Retrieve later (e.g., after a container restart)
if let stored = try await volumeStore.retrieve("my-data") {
    print("Loaded volume:", stored.name)
}

```

This pattern applies identically to containers (`ContainerConfiguration`), networks (`NetworkConfiguration`), and any other managed resource in the system.

## Summary

- **Entity-based storage**: The `EntityStore<T>` protocol in [`Sources/ContainerPersistence/EntityStore.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/EntityStore.swift) defines CRUD operations for all persistent objects.
- **JSON serialization**: `FilesystemEntityStore<T>` writes [`entity.json`](https://github.com/apple/container/blob/main/entity.json) files to type-specific directories under `/var/run/container/`.
- **Resilient loading**: The runtime builds an in-memory index at startup, skipping corrupted files rather than crashing.
- **Volume lifecycle**: `VolumeConfiguration` uses the `isAnonymous` flag to distinguish ephemeral volumes from persistent storage.
- **System aggregation**: `ContainerSystemConfig` coordinates multiple stores to provide a unified view of runtime state.

## Frequently Asked Questions

### How does Apple’s container runtime store container metadata without a database?

The runtime uses `FilesystemEntityStore<T>` to serialize each container object as JSON directly on the host filesystem. Each entity type (containers, volumes, networks) has a dedicated directory under `/var/run/container/`, with individual subdirectories for each resource containing an [`entity.json`](https://github.com/apple/container/blob/main/entity.json) file.

### What happens if an entity.json file is corrupted during startup?

The store logs a warning message identifying the specific corrupted file and continues loading other entities. This failure-isolation design ensures that a single malformed JSON file does not prevent the runtime from starting or managing other containers.

### How does the runtime distinguish between temporary and persistent volumes?

The `VolumeConfiguration` struct includes an `isAnonymous` boolean flag. Anonymous volumes are ephemeral and deleted when their container stops, while persistent volumes lack this flag and remain on disk. The `VolumeResource.isAnonymous` property enforces this behavior at runtime.

### Where does the runtime store persistent data on macOS and Linux?

According to [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift), the runtime stores data under `/var/run/container/<type>` directories, where `<type>` corresponds to the entity category (containers, volumes, networks, etc.). Each type receives an isolated subdirectory to prevent cross-contamination of persistent state.