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

> Learn to build OCI container images on macOS with Apple's container runtime. This guide covers using their CLI for efficient image construction.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-26

---

**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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

- **[`Sources/ContainerCommands/BuildCommand.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/BuildCommand.swift)**: Implements the `container build` CLI, orchestrates the builder lifecycle, and handles image export.
- **[`Sources/ContainerCommands/Builder/BuilderStart.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Builder/BuilderStart.swift)**: Manages VM startup and vsock socket initialization.
- **[`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift)**: Low-level wrapper around the BuildKit daemon; handles the actual build invocation.
- **[`Sources/ContainerBuild/BuildConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/BuildConfig.swift)**: Data structure that translates CLI flags into BuildKit build parameters.
- **[`docs/container-system-config.md`](https://github.com/apple/container/blob/main/docs/container-system-config.md)**: Documents the `[build]` configuration section for VM resource tuning.
- **[`docs/command-reference.md`](https://github.com/apple/container/blob/main/docs/command-reference.md)**: Complete flag reference for `container build`.

## 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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/config.toml)) rather than full QEMU emulation, providing better performance.