# How to Create and Manage Persistent Volumes with Journaling in Apple Container

> Learn to create and manage persistent volumes with EXT4 journaling in Apple Container using the VolumesService API. Enhance data integrity for your applications.

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

---

**Apple Container stores volume data under a dedicated resource root directory and supports EXT4 journaling modes through the `VolumesService` API by passing `journal` driver options when creating volumes.**

The `apple/container` repository provides a native container runtime with persistent storage capabilities. Each volume is backed by a disk image that survives container restarts and system reboots, with optional EXT4 journaling configurations that control data durability and performance characteristics.

## Volume Configuration and Storage Architecture

Persistent volumes in Apple Container are represented by `VolumeConfiguration` records defined in [`Sources/ContainerResource/Volume/VolumeConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Volume/VolumeConfiguration.swift). These records store the volume’s name, driver, filesystem format, host-side source path, creation timestamp, labels, driver-specific options, and optional size limits.

Volume data resides under a **resource root** directory, typically `/var/lib/container`, where each volume receives a dedicated subdirectory containing the backing disk image. The path follows the convention `/var/lib/container/volumes/<name>/volume.img`, ensuring that data persists across system restarts.

## Creating Journaled Volumes with VolumesService

The `VolumesService` class in [`Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift) handles the complete volume lifecycle. When creating a volume, the service exposes the `create(name:driverOpts:labels:)` method, which internally calls `_create(name:driver:driverOpts:labels:)` to instantiate the storage.

To configure journaling, pass a `journal` key in the `driverOpts` dictionary. The service invokes `parseJournalConfig(_:)` to translate the option string into an `EXT4.JournalConfig` instance. This configuration is then passed to `createVolumeImage(for:name:sizeInBytes:journal:)`, which formats the backing image with the specified journaling parameters.

## Supported Journaling Modes

The `journal` driver option accepts the following EXT4 journaling modes. The parser expects strings in the format `"<mode>"` or `"<mode>:<size>"`, where size suffixes support `k`, `m`, `g`, and `t` (binary units).

- **`journal`** – Default EXT4 journaling using ordered mode.
- **`ordered`** – Metadata commits only after data writes flush to disk, providing consistency without full data journaling.
- **`writeback`** – No ordering guarantees between data and metadata writes, offering maximum performance.
- **`writeback:<size>`** – Writeback mode with an explicit journal size (e.g., `writeback:64m`).
- **`none`** – Disables journaling entirely; attempting to use this mode will cause an error.

If the size component is omitted, Apple Container applies a default journal size defined in the EXT4 module.

## Swift API Implementation

Use the `VolumesService` API to programmatically create and inspect journaled volumes.

```swift
import Containerization
import ContainerPersistence
import Logging

let logger = Logger(label: "com.example.volumes")
let volumesService = try await VolumesService(
    resourceRoot: FilePath("/var/lib/container"),
    containersService: containersService,
    log: logger
)

// Create a volume with writeback journaling and 64 MiB journal size
let config = try await volumesService.create(
    name: "my-data-volume",
    driverOpts: ["journal": "writeback:64m"],
    labels: ["app": "my-service"]
)

print("Created volume \(config.name) at \(config.source)")

```

Inspect existing volumes to verify journaling configuration:

```swift
let info = try await volumesService.inspect("my-data-volume")
print("Journal mode:", info.options["journal"] ?? "none")
print("Size:", info.sizeInBytes ?? 0)

```

List all volumes to audit their journaling settings:

```swift
let all = try await volumesService.list()
for v in all {
    print("\(v.name) – driver: \(v.driver) – journal: \(v.options["journal"] ?? "default")")
}

```

## CLI Usage Examples

Create volumes directly from the command line using the `--opt` flag to specify journaling behavior.

Create a volume with ordered journaling:

```bash
container volume create \
    --opt journal=ordered \
    my-ordered-volume

```

Create a volume with a custom journal size:

```bash
container volume create \
    --opt journal=writeback:128m \
    large-writeback-volume

```

## Summary

- Apple Container stores persistent volumes under `/var/lib/container/volumes/<name>/` with disk images created by `VolumesService`.
- The `journal` driver option in [`Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Volumes/VolumesService.swift) supports `ordered`, `writeback`, and `writeback:<size>` modes.
- `VolumeConfiguration` in [`Sources/ContainerResource/Volume/VolumeConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Volume/VolumeConfiguration.swift) tracks volume metadata including journaling options.
- Volume images survive container restarts and system reboots, maintaining data persistence across the runtime lifecycle.

## Frequently Asked Questions

### What happens if I specify an invalid journaling mode?

The `parseJournalConfig(_:)` method in `VolumesService` validates the option string against supported EXT4 modes. If you pass an unsupported value like `none` or malformed syntax, the service throws a validation error before creating the volume image.

### Where does Apple Container store the actual volume data?

The backing disk image resides at `/var/lib/container/volumes/<name>/volume.img` by default, where `<name>` is the volume identifier. This location is determined by the `resourceRoot` parameter passed to `VolumesService` during initialization.

### Can I change the journal mode after creating a volume?

No, the journaling configuration is written during initial volume creation via `createVolumeImage(for:name:sizeInBytes:journal:)`. To change modes, you must create a new volume with the desired `journal` option and migrate data from the existing volume.

### What is the difference between `ordered` and `writeback` journaling?

**`ordered`** mode guarantees that data writes flush to disk before metadata commits, preventing filesystem corruption while offering better performance than full journaling. **`writeback`** mode provides no ordering guarantees, maximizing throughput but risking metadata inconsistency if the system crashes before data writes complete.