# How Configuration Management Works in the Apple Container Project

> Discover how the apple/container project manages its configuration using a layered TOML file system and first-match-wins precedence. Learn about system defaults and user overrides.

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

---

**The apple/container project uses a layered TOML file system with first‑match‑wins precedence, loading system defaults from [`/usr/local/etc/container/config.toml`](https://github.com/apple/container/blob/main//usr/local/etc/container/config.toml) and user overrides from `~/Library/Application Support/com.apple.container/config/config.toml` via the `ConfigurationLoader` class.**

The apple/container repository implements a robust configuration management system that separates system defaults from user customizations. By leveraging TOML files and Swift’s `Codable` protocol, the project ensures type-safe configuration loading across macOS environments. This architecture supports both core system settings and per-plugin configurations through a unified layering mechanism.

## Layered TOML Configuration Architecture

The configuration system employs a **first‑match‑wins** precedence model that merges multiple TOML sources into a single coherent snapshot.

### System-Wide Defaults

Global settings reside in the installation root at [`/usr/local/etc/container/config.toml`](https://github.com/apple/container/blob/main//usr/local/etc/container/config.toml). These values define the baseline behavior for all users on the system and include parameters such as the container root directory and default runtime limits.

### User-Specific Overrides

Individual users can customize behavior by placing overrides in `~/Library/Application Support/com.apple.container/config/config.toml`. According to [`Sources/ContainerPersistence/ConfigurationLoader.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ConfigurationLoader.swift), the loader constructs a prioritized list where the user configuration takes precedence over system defaults.

### Configuration Precedence Rules

When `ConfigurationLoader.load()` executes, it instantiates a `ConfigReader` that merges the configuration layers. The merging process applies user values over system defaults, ensuring that local customizations persist without modifying the global installation.

## Loading Configuration with ConfigurationLoader

The `ConfigurationLoader` class serves as the primary entry point for accessing runtime settings in [`Sources/ContainerPersistence/ConfigurationLoader.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ConfigurationLoader.swift).

To load the merged system configuration asynchronously:

```swift
import ContainerPersistence

// Asynchronously load the merged system configuration.
let systemConfig = try await ConfigurationLoader.load()
print("Container root: \(systemConfig.containerRoot)")

```

This method returns a `ContainerSystemConfig` instance, which is a Swift `Codable` type representing the complete configuration snapshot.

### Plugin-Specific Configuration Loading

For scoped plugin access, the loader provides `ConfigurationLoader.loadForPlugin`, which extracts the `[plugin.<id>]` section from the merged TOML. If no configuration exists, the method returns sensible defaults defined by the plugin’s struct.

```swift
// Loads the `[plugin.my-plugin]` section, falling back to defaults if absent.
let myConfig = try await ConfigurationLoader.loadForPlugin(MyPluginConfig.self)

if myConfig.enabled {
    print("My plugin is enabled with up to \(myConfig.maxInstances) instances.")
}

```

## Plugin Configuration Schema and Validation

Each plugin maintains its own configuration schema through dedicated structures that decode TOML or JSON files.

### PluginConfig Structure

Plugins must define a configuration struct conforming to `LoadablePluginConfiguration`. The `PluginConfig` struct in [`Sources/ContainerPlugin/PluginConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPlugin/PluginConfig.swift) handles the actual decoding logic.

```swift
import ContainerPlugin

// The plugin must expose an identifier.
struct MyPluginConfig: LoadablePluginConfiguration {
    static var pluginId: String { "my-plugin" }

    // Declare fields that appear under `[plugin.my-plugin]` in TOML.
    let enabled: Bool
    let maxInstances: Int

    // Required empty initializer for fall-back when no config exists.
    init() {
        self.enabled = false
        self.maxInstances = 1
    }
}

```

### Configuration File Locations for Plugins

Plugin configurations reside in `<app-root>/user-plugins/<plugin-name>/` and must include either [`config.toml`](https://github.com/apple/container/blob/main/config.toml) or legacy [`config.json`](https://github.com/apple/container/blob/main/config.json). The [`PluginFactory.swift`](https://github.com/apple/container/blob/main/PluginFactory.swift) file locates these configurations by searching for the first valid config file in the plugin directory.

To parse a plugin configuration file directly:

```swift
import ContainerPlugin

let url = URL(fileURLWithPath: "/path/to/user-plugins/example/config.toml")
if let pluginConfig = try PluginConfig(configURL: url) {
    print("Plugin abstract: \(pluginConfig.abstract)")
}

```

## Runtime Immutability and Security

All configuration files are copied into the application root with **read‑only** permissions before the container runtime accesses them. This design prevents running containers from accidentally mutating user-provided settings, ensuring that configuration remains stable throughout the container lifecycle. The `ConfigurationLoader` enforces this constraint when building the configuration snapshot.

## Summary

- The apple/container project uses **TOML files** with a layered architecture combining system defaults and user overrides.
- **ConfigurationLoader** in [`Sources/ContainerPersistence/ConfigurationLoader.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ConfigurationLoader.swift) merges configurations using first‑match‑wins precedence.
- Plugin configurations follow the same pattern, utilizing `PluginConfig` in [`Sources/ContainerPlugin/PluginConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPlugin/PluginConfig.swift) for schema validation.
- Configuration files are treated as **immutable** at runtime, with the loader enforcing read‑only access to prevent accidental modifications.
- Both synchronous and asynchronous loading patterns are supported through Swift’s structured concurrency.

## Frequently Asked Questions

### What format does apple/container use for configuration files?

The project uses **TOML** as the primary configuration format, with legacy support for JSON in plugin configurations. System-wide settings reside in [`/usr/local/etc/container/config.toml`](https://github.com/apple/container/blob/main//usr/local/etc/container/config.toml), while user-specific overrides use `~/Library/Application Support/com.apple.container/config/config.toml`.

### How does the configuration precedence work in apple/container?

The system implements a **first‑match‑wins** layering strategy. The `ConfigurationLoader` builds a prioritized list with user configurations first, followed by system defaults. When merged via `ConfigReader`, user values override system settings, allowing granular customization without affecting global installations.

### Where are plugin-specific configurations stored?

Plugin configurations live in `<app-root>/user-plugins/<plugin-name>/config.toml` (or [`config.json`](https://github.com/apple/container/blob/main/config.json)). The [`PluginFactory.swift`](https://github.com/apple/container/blob/main/PluginFactory.swift) source file handles discovery by locating the first configuration file in the plugin directory, while `PluginConfig` validates the schema during initialization.

### How do I load configuration for a custom plugin?

Implement the `LoadablePluginConfiguration` protocol in your plugin struct, defining a static `pluginId` property that matches your TOML section name (e.g., `[plugin.my-plugin]`). Then call `ConfigurationLoader.loadForPlugin(MyPluginConfig.self)` to retrieve the scoped configuration with automatic fallback to default values.