# How to Optimize Container Performance with CPU and Memory Limits in Apple Container

> Optimize container performance using CPU and memory limits in Apple Container. Learn to leverage --cpus and --memory flags with Linux cgroups to prevent resource contention and meet workload needs.

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

---

**Optimize container performance by overriding the default 4 CPU cores and 1 GiB RAM with the `--cpus` and `--memory` flags, which enforce limits via Linux cgroups to prevent resource contention and match workload requirements.**

The `apple/container` repository provides a lightweight virtual-machine container runtime that defaults to conservative resource allocation. While these defaults ensure host stability, compute-intensive workloads often require explicit tuning to maximize throughput and prevent throttling.

## Understanding Default Resource Limits

By default, each container receives **4 CPU cores** and **1 GiB of memory** through the `Resources` struct defined in [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift). These values strike a balance between container performance and host responsiveness on typical developer hardware.

The default initialization occurs at lines 53-56, where the struct defines these baseline values:

```swift
public struct Resources {
    public var cpus: Int = 4
    public var memoryInBytes: UInt64 = 1 * 1024 * 1024 * 1024  // 1 GiB
    // ...
}

```

When you optimize container performance with CPU and memory limits, you override these defaults to align resources with specific workload demands.

## Configuring Limits via Command Line

The most common method to adjust resources uses the `--cpus` and `--memory` flags with `container run` or `container create` commands.

To allocate 8 CPUs and 32 GiB of RAM:

```bash
container run --rm --cpus 8 --memory 32g my-image

```

For builder containers that require additional resources:

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

```

The CLI parser converts human-readable memory strings (like `32g`) into bytes through the `memoryStringAsBytes` function in [`Sources/Services/ContainerAPIService/Client/Parser.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Parser.swift) (lines 49-56). This allows you to specify values using `g`, `m`, or `k` suffixes rather than raw byte counts.

## Runtime Enforcement and Validation

### Minimum Memory Validation

Before applying limits, the runtime validates that memory allocation meets safety thresholds. In [`Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift) (lines 329-332), a guard clause rejects any container configuration below **200 MiB**:

```swift
guard config.resources.memoryInBytes >= 200 * 1024 * 1024 else {
    throw ContainerError.invalidMemorySize
}

```

This prevents accidental undersizing that could cause immediate out-of-memory crashes.

### Cgroup Enforcement Mechanism

The actual resource isolation occurs through Linux cgroups. When `ContainersService` starts a container, it copies the parsed resources into the runtime configuration at [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift) (lines 355-356):

```swift
czConfig.memoryInBytes = config.resources.memoryInBytes
czConfig.cpus = config.resources.cpus

```

The runtime then writes these values to cgroup control files:

- **CPU limits**: Written to `/sys/fs/cgroup/cpu.max` as a quota proportional to the number of CPUs. The denominator defaults to 100,000 microseconds, with the numerator calculated as `cpus × 100,000`. For example, 8 CPUs results in `800000 100000` in the cgroup file.
- **Memory limits**: Written to `/sys/fs/cgroup/memory.max` in bytes.

You can verify these limits inside a running container by reading the cgroup files directly, as demonstrated in [`Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift`](https://github.com/apple/container/blob/main/Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift) (lines 173-186):

```bash
cat /sys/fs/cgroup/cpu.max    # Output: "800000 100000" for 8 CPUs

cat /sys/fs/cgroup/memory.max   # Output: "34359738368" for 32 GiB

```

## Programmatic Resource Configuration

For Swift-based tooling, instantiate `ContainerConfiguration.Resources` directly to optimize container performance:

```swift
import ContainerizationOCI

let resources = ContainerConfiguration.Resources(
    cpus: 8,
    memoryInBytes: 32 * 1024 * 1024 * 1024,  // 32 GiB
    storage: nil,
    cpuOverhead: 1
)

let config = ContainerConfiguration(
    id: "optimized-container",
    image: ImageDescription(name: "my-image"),
    process: ProcessConfiguration(entrypoint: [])
)
config.resources = resources

```

This approach, defined in [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift) (lines 62-68), allows fine-grained control when embedding the container runtime in larger applications.

## Summary

- **Default resources**: 4 CPUs and 1 GiB RAM defined in [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift)
- **CLI flags**: Use `--cpus` and `--memory` to override defaults, with parsing handled in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift)
- **Safety minimum**: 200 MiB memory floor enforced in [`ContainersService.swift`](https://github.com/apple/container/blob/main/ContainersService.swift)
- **Kernel enforcement**: cgroups apply limits via `cpu.max` and `memory.max` configured in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift)
- **Verification**: Inspect `/sys/fs/cgroup/` files to confirm limits are active

## Frequently Asked Questions

### What are the default CPU and memory limits in Apple Container?

By default, each container receives **4 CPU cores** and **1 GiB of memory** (1,073,741,824 bytes). These values are hardcoded in the `Resources` struct within [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift) to provide a balanced starting point for general development workloads.

### How does the container runtime enforce CPU limits?

The runtime enforces CPU limits through Linux cgroups by writing a calculated quota to `/sys/fs/cgroup/cpu.max`. The quota numerator equals `cpus × 100,000` microseconds, while the denominator remains 100,000. For example, 2 CPUs results in `200000 100000` in the cgroup file, limiting the container to proportional time slices of CPU access.

### What is the minimum memory limit allowed?

The runtime rejects any container configuration with less than **200 MiB** (209,715,200 bytes) of memory. This validation occurs in [`Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift) to prevent configurations that would immediately trigger out-of-memory killer events.

### How can I verify resource limits inside a running container?

Inspect the cgroup virtual files directly by running `cat /sys/fs/cgroup/cpu.max` and `cat /sys/fs/cgroup/memory.max` inside the container. These files show the actual quota values written by the runtime, confirming that your `--cpus` and `--memory` flags were applied correctly.