How Container Handles Multi-Platform Image Builds with Rosetta on Apple Silicon
Container leverages a dedicated BuildKit builder VM running under Rosetta translation to execute x86-64 binaries on Apple Silicon, enabling seamless cross-platform OCI image builds without manual emulation configuration.
The apple/container tool simplifies multi-platform image builds on Apple Silicon by integrating Rosetta translation directly into its build pipeline. When you invoke container image build with a --platform flag, the tool orchestrates a specialized builder container that targets architectures like linux/amd64 while executing natively on ARM64 hardware. This architecture combines Rosetta's transparent binary translation with BuildKit's native cross-platform capabilities to produce multi-architecture manifests without requiring separate QEMU infrastructure.
Platform Selection and Precedence Rules
Parsing the --platform Flag
When you specify --platform <os>/<arch> (for example, --platform linux/amd64), the CLI stores this request in the ContainerAPIService.DefaultPlatform helper. According to the source code in Sources/Services/ContainerAPIService/Client/DefaultPlatform.swift, the precedence rules follow a strict hierarchy:
--platformflag overrides everythingCONTAINER_DEFAULT_PLATFORMenvironment variable serves as the fallback- Host defaults derived from the local system architecture provide the final default
This resolved value propagates to ClientImage.fetch, ensuring the builder retrieves the correct architecture-specific manifest from multi-arch base images before the build begins.
Manifest Resolution
The fetch routine uses the resolved platform identifier to select the appropriate image manifest from registry manifest lists. This guarantees that when building for amd64 on an Apple Silicon host, the builder pulls AMD64 variants of base images rather than ARM64 variants, maintaining architectural consistency throughout the build process.
Builder Container Configuration and Rosetta Integration
Rosetta vs. QEMU Decision Logic
The core logic for enabling Rosetta translation resides in Sources/ContainerCommands/Builder/BuilderStart.swift within the BuilderStart.start method. When configuring the builder container, the code evaluates the containerSystemConfig.build.rosetta boolean:
// BuilderStart.swift (line 96-100)
let useRosetta = containerSystemConfig.build.rosetta
let shimArguments = [
"--debug",
"--vsock",
useRosetta ? nil : "--enable-qemu",
].compactMap { $0 }
// ...
config.rosetta = useRosetta
When useRosetta evaluates to true, the builder container runs with Rosetta translation enabled. The container runtime starts the builder VM on Apple Silicon hardware, and the Rosetta translation layer automatically converts any x86-64 binaries that BuildKit launches during the build process.
If Rosetta is disabled via --no-rosetta or configuration files, the shim receives the --enable-qemu argument instead, causing the container runtime to start the VM with QEMU-based emulation rather than Rosetta translation.
Configuration Sources
The default Rosetta behavior is controlled by BuildConfig.defaultRosetta in Sources/ContainerPersistence/ContainerSystemConfig.swift (line 81), where the default value is set to true. Users can explicitly disable this behavior through:
- The
--no-rosettaCLI flag - Setting
rosetta=falseinconfig.toml
Cross-Platform Build Delegation to BuildKit
BuildKit Multi-Platform Support
Container delegates the actual multi-architecture build to BuildKit running inside the configured builder container. BuildKit accepts a list of target platforms propagated from the --platform flag and performs the following operations:
- Pulls the appropriate base images for each requested platform
- Executes build instructions using the available architecture binaries (translated via Rosetta when targeting x86-64 on Apple Silicon)
- Produces a manifest list containing architecture-specific layers for all requested platforms
Because the builder container runs under Rosetta, x86-64 BuildKit binaries can execute on Apple Silicon hosts without modification. This allows a single container image build command to emit images for both arm64 and amd64 (or any other supported platform) while the host handles the architecture translation transparently.
Practical Examples
Build a multi-architecture image for AMD64 using Rosetta translation (default behavior):
container image build \
--platform linux/amd64 \
--tag myrepo/example:latest \
./path/to/Dockerfile
Force QEMU emulation instead of Rosetta for the same build:
container image build \
--platform linux/amd64 \
--no-rosetta \
--tag myrepo/example:latest \
./path/to/Dockerfile
Summary
- Platform precedence follows the order:
--platformflag >CONTAINER_DEFAULT_PLATFORMenvironment variable > host system defaults, as implemented inDefaultPlatform.swift. - Rosetta translation is enabled by default via
BuildConfig.defaultRosettainContainerSystemConfig.swift, allowing x86-64 binaries to run natively on Apple Silicon through automatic translation. - Builder configuration in
BuilderStart.startswitches between Rosetta and QEMU by conditionally passing--enable-qemuto the container shim when Rosetta is disabled. - BuildKit delegation handles the actual multi-platform image construction, producing manifest lists that contain architecture-specific layers for each requested platform.
Frequently Asked Questions
How does Container choose between Rosetta and QEMU for x86-64 builds?
Container checks the rosetta configuration value (defaulting to true in ContainerSystemConfig.swift) when starting the builder container in BuilderStart.start. If enabled, the builder VM runs with Rosetta translation; if disabled via --no-rosetta or configuration, the system passes --enable-qemu to the container shim to use QEMU emulation instead.
Can I build for multiple architectures simultaneously with Container?
Yes. BuildKit supports multi-platform builds natively, and Container propagates the --platform flag to BuildKit running inside the builder container. BuildKit will pull the appropriate base images for each architecture and produce a manifest list containing all requested variants, whether you specify linux/amd64, linux/arm64, or multiple platforms.
What happens if I don't specify a --platform flag?
If you omit the --platform flag, Container falls back to the CONTAINER_DEFAULT_PLATFORM environment variable, and finally to the host's native architecture as determined by DefaultPlatform.swift. This ensures builds target the local machine architecture by default while maintaining the ability to override for cross-platform development.
Where is the Rosetta default setting stored in the codebase?
The default value of true for Rosetta support is defined in Sources/ContainerPersistence/ContainerSystemConfig.swift at line 81 within the BuildConfig structure. This value is accessed via containerSystemConfig.build.rosetta when BuilderStart.start configures the builder container.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →