How to Build Container Images with Apple's Container Runtime: A Complete Guide

Apple's container CLI orchestrates a BuildKit-based builder inside a lightweight virtual machine on macOS 26 (Apple silicon), enabling OCI image builds through a simple command-line interface that streams build contexts over vsock and exports to local stores or tarballs.

The apple/container repository provides a native container runtime for macOS that wraps a BuildKit daemon running inside a lightweight VM. When you build container images with Apple's container runtime, the tool handles VM lifecycle management, context streaming, and image export automatically. This guide breaks down the build process using the actual source implementation and provides production-ready examples.

How the Build Process Works

The container build command defined in Sources/ContainerCommands/BuildCommand.swift executes a six-phase workflow that abstracts VM management while exposing standard BuildKit features.

VM Initialization via Vsock

The CLI first ensures the builder VM is running by attempting to dial the BuildKit socket on vsock port 8088. If the socket is unresponsive, the code invokes BuilderStart.start() from Sources/ContainerCommands/Builder/BuilderStart.swift to launch the VM using the resource constraints defined in ~/.config/container/config.toml (CPU count, memory limits, and Rosetta settings).

Build Context Preparation

The command reads your Dockerfile (or Containerfile) from the path specified by -f/--file or from STDIN (-). The source code enforces a 16 KiB size limit on the Dockerfile (see the guard at lines 55-58 in BuildCommand.swift). The CLI then creates a temporary build directory on the host and prepares to stream the context to the builder.

Build Configuration Assembly

The run() method instantiates a Builder.BuildConfig object (lines 55-73 in BuildCommand.swift) that maps CLI flags to BuildKit parameters:

  • Architecture and OS: --arch, --os, and --platform for multi-arch builds
  • Build arguments: --build-arg key-value pairs
  • Secrets: Injected via SecretType.data from environment variables or files
  • Cache options: Honor --no-cache directives
  • Resource limits: Memory and CPU constraints passed to the builder

Build Execution and Progress Tracking

The CLI streams the build context to the builder over the vsock connection and invokes BuildKit. Progress is rendered using a ProgressBar that supports three output styles controlled by --progress (auto, plain, or tty). Use -q/--quiet to suppress output entirely.

Image Export and Tagging

After the build completes, the CLI handles export via Builder.BuildExport:

  • OCI (default): Stores the image in the local OCI store
  • Tar: Creates a portable tarball via --output type=tar,dest=path
  • Local: Writes a directory layout with --output type=local

The CLI then unpacks the image, applies all --tag arguments, and prints the final tag(s) or file path to stdout.

Configuring the Builder VM

The builder VM is configurable through the [build] section in ~/.config/container/config.toml. Key parameters include CPU count, memory allocation, and Rosetta enablement for x86_64 emulation. You can pre-start the VM with custom resources using the container builder start command, which persists these settings for subsequent builds.

Building Container Images: Practical Examples

Basic Local Build

Build an image from the current directory and store it in the local OCI store:

container build --tag my-app:latest .

This uses the default VM configuration (2 CPU, 2 GiB RAM) and the default oci exporter.

Multi-Architecture Build

Create a multi-arch manifest for both ARM64 and AMD64:

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

The builder generates a manifest list supporting both architectures.

Custom Builder Resources

Provision a high-performance VM for large builds:

container builder start --cpus 8 --memory 32g
container build --tag heavy-build .

The builder start command updates the VM specs before the build executes.

Reproducible Build Without Cache

Disable BuildKit cache for clean builds:

container build --no-cache --tag clean-build .

This sets noCache = true in Builder.BuildConfig (line 93 of the source).

Export as Tarball

Create a portable tarball instead of loading into the local store:

container build \
  --output type=tar,dest=/tmp/my-app.tar \
  --tag export-build .

The resulting tarball contains the OCI image layout at the specified destination.

Using Build-Time Secrets

Inject secrets from environment variables safely:

container build \
  --secret id=mykey,env=MY_SECRET_ENV \
  --tag secret-build .

The secret value is read from MY_SECRET_ENV and passed to the build via SecretType.data without appearing in layer history.

Key Source Files and Architecture

Understanding these files helps when debugging or extending the build process:

Summary

  • Apple's container runtime uses a BuildKit-based builder inside a lightweight VM on macOS 26 (Apple silicon) to build OCI images.
  • The vsock connection on port 8088 provides the communication channel between the CLI and the builder VM.
  • BuildConfig in BuildCommand.swift maps CLI flags (architecture, secrets, cache options) to BuildKit parameters.
  • The 16 KiB limit applies to Dockerfile content read from disk or STDIN.
  • Export options include oci (local store), tar (portable archive), and local (directory layout).
  • The builder VM is configurable via ~/.config/container/config.toml and can be pre-started with container builder start.

Frequently Asked Questions

What macOS version is required to use Apple's container runtime?

Apple's container runtime requires macOS 26 running on Apple silicon. The builder VM uses virtualization features specific to this version and hardware architecture.

How does the CLI communicate with the BuildKit daemon?

The CLI communicates over a vsock socket on port 8088. The BuilderStart.start() function ensures this socket is available before the build begins, launching the VM if necessary.

Why is there a 16 KiB limit on Dockerfiles?

The source code at lines 55-58 of BuildCommand.swift imposes a 16 KiB limit on Dockerfile content to prevent memory pressure when reading build definitions from STDIN or disk. For larger build definitions, consider using multi-stage builds or external configuration files mounted as build contexts.

Can I build multi-architecture images without QEMU?

Yes. The builder VM supports native multi-architecture builds for arm64 and amd64 using the --arch flag. For x86_64 builds on Apple silicon, the runtime can optionally leverage Rosetta (configured in config.toml) rather than full QEMU emulation, providing better performance.

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 →