# How to Optimize Container Build Performance with BuildKit Resource Limits

> Accelerate container builds by optimizing BuildKit resource limits. Tune CPU and memory allocation with flags or config.toml for faster parallel compilation and caching.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: performance
- Published: 2026-06-21

---

**Tune the BuildKit builder's CPU and memory allocation using the `--cpus` and `--memory` flags or the [`config.toml`](https://github.com/apple/container/blob/main/config.toml) file to significantly accelerate container builds by giving the virtual machine more resources for parallel compilation and in-memory caching.**

The `apple/container` project runs BuildKit inside a dedicated container within a lightweight macOS virtual machine, and optimizing these resource constraints directly impacts build speed. By adjusting the CPU core count and memory allocation, you enable BuildKit to parallelize compilation steps and maintain larger dependency caches, reducing overall build time for CPU- or memory-intensive Dockerfiles.

## Understanding BuildKit Resource Allocation

The `container` CLI manages a dedicated **BuildKit** container (the *builder*) that runs inside a macOS VM. When you initiate a build, the tool internally invokes **`BuilderStart`** from [`Sources/ContainerCommands/Builder/BuilderStart.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Builder/BuilderStart.swift), which parses resource limits and applies them to the virtual machine's hypervisor settings.

The resource limits are ultimately enforced through the **`ContainerConfiguration`** struct, where `cpus` and `memoryInBytes` map directly to the VM's allocation. Increasing these values gives BuildKit more computational power to execute parallel build steps and cache layers in memory rather than writing to disk.

## Setting Resource Limits via CLI Flags

For one-off builds or CI pipelines, pass the `--cpus` and `--memory` flags directly to the `container build` command. These flags override any default settings defined in your configuration file.

```bash

# Allocate 4 CPUs and 8 GiB of RAM for a single build

container build --cpus 4 --memory 8g .

# Or start the builder explicitly with specific resources

container builder start --cpus 4 --memory 8g

```

The [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift) file handles this by calling **`Parser.resources`**, which validates the inputs and converts memory strings (like `8g`) into bytes. If the builder container already exists, the code compares the requested resources against the running instance's current allocation (`existingResources.cpus` vs `resources.cpus`, `existingResources.memoryInBytes` vs `resources.memoryInBytes`). A mismatch triggers a graceful stop-and-delete of the old container, followed by recreation with the new limits.

## Configuring Persistent Defaults in config.toml

To set default resource limits for all builds, modify the `[build]` section in `~/.config/container/config.toml`. These values persist across sessions and are used whenever you omit the CLI flags.

```bash
mkdir -p ~/.config/container
cat >> ~/.config/container/config.toml <<EOF
[build]
cpus = 4
memory = "8g"
EOF

```

According to the source code in [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift), the system merges these configuration values with the CLI flags using the following precedence: user-supplied flags override the config file, which in turn overrides hardcoded defaults. The merged values are then passed to `Parser.resources` along with the defaults:

```swift
let resources = try Parser.resources(
    cpus: cpus,
    memory: memory,
    defaultCPUs: defaultBuildCPUs,
    defaultMemory: defaultBuildMemory
)

```

## How the Builder Recreation Logic Works

The `apple/container` repository includes intelligent recreation logic around **lines 70-84** of [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift). When you request a build, the code checks whether the existing builder container matches the requested configuration across five dimensions: image, CPU, memory, environment variables, and DNS settings.

If any resource limit differs from the currently running instance, the tool automatically stops and removes the old container before creating a new one with the updated `ContainerConfiguration.resources`. This ensures that your resource adjustments take effect immediately without requiring manual intervention.

The Swift implementation validates this comparison before calling `client.create(...)`:

```swift
var config = ContainerConfiguration(id: Builder.builderContainerId,
                                    image: imageDesc,
                                    process: processConfig)
config.resources = resources  // ← resource limits applied here

```

## Practical Code Examples

### Bash: High-Performance Build Configuration

```bash

# Optimize for a multi-stage build with heavy compilation

container build --cpus 8 --memory 16g --tag myapp:latest .

```

### Swift: Programmatic Resource Configuration

When interacting with the Container API client directly, configure resources as follows:

```swift
let resources = try Parser.resources(
    cpus: 4,                         // user-requested CPUs
    memory: "8g",                 // user-requested memory
    defaultCPUs: 2,               // fallback from config.toml
    defaultMemory: MemorySize("2g") // fallback from config.toml
)

var cfg = ContainerConfiguration(id: "buildkit",
                                 image: imageDesc,
                                 process: processConfig)
cfg.resources = resources            // limits attached to VM configuration

```

## Summary

- **Resource limits** in `apple/container` control the BuildKit builder's CPU cores and RAM allocation within the macOS VM.
- **CLI flags** (`--cpus` and `--memory`) provide temporary overrides for specific builds.
- **Configuration file** settings in `~/.config/container/config.toml` establish persistent defaults under the `[build]` section.
- **Automatic recreation** logic in [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift) (lines 70-84) compares existing and requested resources, restarting the builder when limits change.
- **Performance gains** come from increased parallelization and larger in-memory caches, particularly for complex multi-stage builds.

## Frequently Asked Questions

### What is the default CPU and memory allocation for the BuildKit builder?

The default values are defined in the system configuration (`containerSystemConfig.build.cpus` and `containerSystemConfig.build.memory`) and vary by installation. If not specified in `~/.config/container/config.toml`, the builder typically starts with conservative defaults (often 2 CPUs and 2-4 GiB of memory), which you can verify by checking the fallback values in [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift).

### Do I need to manually restart the builder after changing resource limits?

No. The `container` tool automatically handles recreation. When you invoke `container build` or `container builder start` with different resource flags, the code in [`BuilderStart.swift`](https://github.com/apple/container/blob/main/BuilderStart.swift) compares the requested resources against the running instance (`existingResources.cpus` and `existingResources.memoryInBytes`). If they differ, the tool gracefully stops and deletes the existing container before creating a new one with the updated limits.

### Can I use environment variables instead of CLI flags?

The `apple/container` tool does not use environment variables like `BUILDKIT_CPU` or `BUILDKIT_MEMORY`. Instead, it relies on the `--cpus` and `--memory` CLI flags or the [`config.toml`](https://github.com/apple/container/blob/main/config.toml) file. For CI pipelines, you can script the CLI flags or generate a temporary config file to inject resource settings dynamically.

### How does increasing memory allocation specifically improve build performance?

Higher memory limits allow BuildKit to maintain larger in-memory layer caches and enable more aggressive parallelization during compilation. When the builder runs inside the macOS VM, additional RAM reduces the need to swap build cache to disk, significantly speeding up dependency fetching and multi-stage builds that process large artifacts.