Running Memory-Intensive Containers with Apple Container: Best Practices and Configuration

Use the --memory flag to allocate sufficient RAM to the Linux VM, configure the builder VM separately with container builder start --memory, and restart containers periodically to reclaim host memory since the macOS Virtualization framework does not support full memory ballooning.

Apple Container (apple/container) runs each container inside a lightweight Linux VM created by the macOS Virtualization framework. Unlike traditional container runtimes that allocate memory directly to processes, memory is reserved at the VM level, which requires specific configuration strategies for memory-intensive workloads.

Understanding the VM-Based Memory Architecture

Containers in apple/container execute within isolated Linux VMs rather than directly on the host kernel. According to the source code in Sources/ContainerPersistence/ContainerSystemConfig.swift, the default VM allocation is 1 GiB of RAM and 4 CPUs for both containers and the builder. This architecture provides strong isolation but introduces unique constraints: memory allocated to a VM remains claimed by that VM until it stops, regardless of whether the container processes inside have freed that memory.

Allocating Memory for Individual Containers

To run memory-intensive applications, you must explicitly override the default 1 GiB limit using the --memory (or -m) flag on every container run, container build, or container builder start command.

The flag accepts size suffixes (K, M, G, T, P) parsed by MemorySize.swift in Sources/ContainerPersistence/MemorySize.swift. The parser validates input through Parser.memoryStringAsMiB in Sources/Services/ContainerAPIService/Client/Parser.swift, which enforces a minimum allocation of 200 MiB and ensures the value fits into a 64-bit integer. The final value is stored in memoryInBytes within ContainerConfiguration.swift (Sources/ContainerResource/Container/ContainerConfiguration.swift) and passed to the VM runtime.


# Run a container with 12 GiB of RAM

container run --rm --cpus 8 --memory 12g my-big-image

Configuring the Builder VM

The build environment runs in its own separate VM that also defaults to 1 GiB. For memory-intensive builds, increase the builder resources before starting the build process:


# Start the builder with 32 GiB of RAM

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

As documented in docs/how-to.md, the builder VM persists until explicitly stopped, so resource changes require restarting the builder.

Adjusting Default Machine Configuration

Rather than specifying memory per command, you can modify the underlying machine configuration that applies to all containers. The default machine uses "half of host memory" as specified in MachineConfig.swift (Sources/ContainerPersistence/MachineConfig.swift).

To permanently change the default allocation for all future containers:


# Set machine default to 16 GiB

container machine set memory=16G

Details are available in docs/container-machine.md. This setting affects the underlying VM infrastructure rather than individual container instances.

Monitoring Memory Usage

Track real-time consumption using container stats or container top to ensure your allocations match actual usage. According to ContainerStats.swift (Sources/ContainerCommands/Container/ContainerStats.swift), the output displays memoryUsageBytes versus memoryLimitBytes, allowing you to identify when containers approach their limits.


# Monitor live memory statistics

container stats my-big-image

Managing Memory Reclamation

Due to limitations in the macOS Virtualization framework, full memory ballooning is not supported. Pages freed inside the VM remain allocated to the VM from the host perspective until the VM stops. Consequently, long-running memory-intensive containers can cause the host to appear to leak RAM.

To reclaim memory on the host system:


# Restart the container to free VM memory

container stop my-big-image && container start my-big-image

As explained in docs/technical-overview.md, periodic restarts are the only method to return freed memory to macOS until ballooning support is implemented.

Summary

  • Always specify --memory (e.g., --memory 12g) when starting containers or builders to override the 1 GiB default defined in ContainerSystemConfig.swift.
  • Configure the builder separately using container builder start --memory before running large builds.
  • Set machine defaults with container machine set memory= to avoid repeating flags for every command.
  • Monitor with container stats to compare memoryUsageBytes against memoryLimitBytes.
  • Restart containers periodically to reclaim host memory, as the Virtualization framework lacks ballooning support.

Frequently Asked Questions

What is the minimum memory allocation for containers?

The minimum memory allocation is 200 MiB. This is enforced by Parser.memoryStringAsMiB in Sources/Services/ContainerAPIService/Client/Parser.swift, which validates all user-provided memory strings and rejects values below this threshold.

Why doesn't memory return to macOS after my container frees it inside the VM?

The macOS Virtualization framework does not currently support full memory ballooning. Once memory is allocated to the Linux VM, it remains assigned to that VM until it stops, regardless of whether the container processes have freed those pages. Restarting the container is required to return memory to the host.

Should I configure memory for the builder or the container?

Configure both. The builder VM (set via container builder start --memory) handles compilation and image building, while container runtime memory (set via container run --memory) handles execution. They operate in separate VMs with independent memory pools, so each requires explicit configuration for memory-intensive tasks.

How do I make memory settings persistent across all container commands?

Use container machine set memory=<size> to change the default machine configuration stored in MachineConfig.swift. This applies to all future containers and builders, whereas --memory flags apply only to individual commands.

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 →