How Configuration Management Works in the Apple Container Project

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 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. 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, 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.

To load the merged system configuration asynchronously:

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.

// 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 handles the actual decoding logic.

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 or legacy config.json. The 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:

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 merges configurations using first‑match‑wins precedence.
  • Plugin configurations follow the same pattern, utilizing PluginConfig in 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, 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). The 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.

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 →