How container build Handles Multi-Architecture Builds Using the --arch Flag

The container build command aggregates comma-separated architecture values from the --arch flag, combines them with the target operating system (defaulting to Linux), and passes a Set<Platform> to BuildKit to generate a multi-architecture manifest list.

The container build command in the apple/container repository provides native support for multi-architecture container images through its --arch flag. By transforming user input into platform-specific build configurations, the command enables BuildKit to produce manifest lists containing separate image variants for each requested architecture. This implementation leverages Swift's argument parsing capabilities and sophisticated platform resolution logic to streamline cross-platform container development.

How the --arch Flag Works in container build

Option Parsing and Default Values

In Sources/ContainerCommands/BuildCommand.swift, the --arch flag is implemented as an @Option that accepts comma-separated values and can be specified multiple times. The declaration appears at lines 54-62:

@Option(
    name: .shortAndLong,
    help: ArgumentHelp("Add the architecture type to the build",
                      valueName: "value"),
    transform: { val in val.split(separator: ",").map { String($0) } }
)
var arch: [[String]] = {
    [[Arch.hostArchitecture().rawValue]]
}()

The transform closure splits comma-separated input into individual strings, while the default closure initializes the value to the host architecture using Arch.hostArchitecture().rawValue. When users specify --arch multiple times, each occurrence appends a new sub-array, resulting in a nested array structure like [["arm64"], ["amd64"]] for the invocation --arch arm64 --arch amd64.

Platform Resolution Logic

The platform resolution occurs in BuildCommand.swift between lines 423-447. The command evaluates platform specifications in strict priority order:

  1. Explicit --platform flags (highest priority)
  2. CONTAINER_DEFAULT_PLATFORM environment variable (fallback)
  3. Combined --os and --arch values (default when no other platform specified)

When falling back to the OS/arch combination, the code iterates through all collected architecture values:

for o in (self.os.flatMap { $0 }) {
    for a in (self.arch.flatMap { $0 }) {
        guard let platform = try? Platform(from: "\(o)/\(a)") else {
            throw ValidationError("invalid os/architecture combination \(o)/\(a)")
        }
        results.insert(platform)
    }
}

This creates a Platform instance for every OS-architecture pair, defaulting to linux when --os is not specified. The resulting Set<Platform> eliminates duplicates while preserving all unique platform combinations requested by the user.

BuildKit Integration for Multi-Arch Output

After platform resolution, the command passes the platform set to the builder configuration at lines 664-670:

let config = Builder.BuildConfig(
,
    platforms: [Platform](platforms),

)

BuildKit receives this array and constructs a multi-architecture manifest list, building each variant in parallel when possible. The final image contains separate layers for each architecture under a single tag, enabling runtime selection via the --arch flag in container run.

Practical Examples for Multi-Architecture Builds

You can specify architectures using multiple flags or comma-separated values:

Multiple flag approach:

container build \
    --arch arm64 \
    --arch amd64 \
    --tag registry.example.com/app:latest \
    --file Dockerfile .

Comma-separated values:

container build \
    --arch arm64,amd64 \
    --tag registry.example.com/app:latest \
    .

Running specific variants:

container run --arch arm64 registry.example.com/app:latest uname -a
container run --arch amd64 registry.example.com/app:latest uname -a

Key Source Files and Implementation Details

Understanding the multi-architecture implementation requires examining these specific files:

Summary

  • The --arch flag accepts comma-separated values and can be specified multiple times, with a default value matching the host architecture.
  • Platform resolution follows a strict priority: --platform > CONTAINER_DEFAULT_PLATFORM > combined --os/--arch.
  • The command generates a Set<Platform> from OS-architecture pairs and passes it to BuildKit via Builder.BuildConfig.
  • BuildKit produces a manifest list containing discrete image variants for each requested architecture.
  • Unit tests in CLIBuilderTest.swift validate the flag parsing and platform resolution behavior.

Frequently Asked Questions

Can I mix comma-separated and multiple --arch flags in the same command?

Yes. The transform closure splits each flag's value by commas, and multiple flag occurrences append to the nested array. Both --arch arm64,amd64 and --arch arm64 --arch amd64 produce equivalent platform sets after the flattening operation in the resolution logic.

What happens if I don't specify the --arch flag?

When omitted, the default closure initializes the architecture to the host's native architecture using Arch.hostArchitecture().rawValue. The build produces a single-architecture image for the detected host platform, ensuring compatibility with the machine running the command.

Does the --arch flag override the --platform flag?

No. The platform resolution logic explicitly checks --platform first (lines 423-427 in BuildCommand.swift). If --platform is present, the command ignores both --os and --arch values entirely, using only the explicitly specified platform string.

How does the container tool validate architecture combinations?

During the resolution loop (lines 436-440), the command attempts to initialize a Platform instance with the format "\(os)/\(architecture)". If the combination is invalid, it throws a ValidationError with the message "invalid os/architecture combination" before reaching BuildKit, preventing wasted build cycles on unsupported platforms.

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 →