# Limitations of Memory Ballooning in Container VMs: Why macOS VMs Can't Shrink Memory

> Discover why macOS VMs in apple/container can only grow memory, not shrink it. Learn about memory ballooning limitations and how to reclaim memory without full restarts.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: deep-dive
- Published: 2026-07-12

---

**Memory ballooning in the apple/container project only supports upward memory growth; VMs cannot return freed pages to macOS, requiring full restarts to reclaim memory.**

Memory ballooning in container VMs allows dynamic memory allocation, but the apple/container implementation faces significant constraints. While the macOS Virtualization framework can grow a VM's memory footprint on demand, it lacks the ability to shrink memory back to the host. These limitations of memory ballooning in container VMs create operational challenges for long-running workloads and multi-container environments.

## How Memory Ballooning Works in the macOS Virtualization Framework

The macOS Virtualization framework provides memory ballooning capabilities that allow virtual machines to dynamically adjust their memory usage. However, as implemented in the container project, this feature represents only a partial implementation of the full specification. The system can provision additional memory to a VM when container workloads demand it, but the reverse operation—returning unused memory to the host—is not supported.

## The Five Critical Limitations of Memory Ballooning in Container VMs

### Partial Ballooning Support

The framework can grow the VM's memory when container workloads demand it, but it cannot shrink the VM by returning freed pages to macOS. According to [`docs/technical-overview.md`](https://github.com/apple/container/blob/main/docs/technical-overview.md) (lines 57-60), once the VM allocates a page, that memory stays reserved for the VM even after container processes release it internally. This asymmetry means that memory allocation is strictly monotonic during a VM's lifetime.

### No Host-Side Reclamation

Memory pages that Linux inside the container frees are never handed back to the host operating system. When processes inside the container release memory, the Linux kernel marks those pages as free, but the macOS Virtualization framework does not reclaim them. This means that if many containers run memory-intensive workloads, the host's overall memory usage remains elevated until the containers or VM are restarted.

### Restart Required for Memory Reduction

Because the VM cannot shrink on its own, the only way to reduce its resident memory footprint is to stop and recreate the container or restart the entire VM. This creates operational overhead for long-running services that need to scale down memory usage after completing peak workloads. The memory configuration applied in [`Sources/Services/MachineAPIService/Server/MachinesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Server/MachinesService.swift) via `bootConfig.memory` persists until the VM lifecycle ends.

### macOS Version Constraints

Full ballooning capabilities only work on macOS 26 (the version shipping with the latest Apple Silicon). On macOS 15, the framework lacks the newer APIs required for reliable ballooning operation, making the feature unavailable or unstable on older releases. As noted in [`docs/technical-overview.md`](https://github.com/apple/container/blob/main/docs/technical-overview.md) (line 61), users on earlier macOS versions cannot utilize the limited ballooning features at all.

### Impact on Multi-Container Environments

When several containers share a single VM, the inability to release memory means aggregate memory footprints can quickly exceed host capacity. Administrators must manually stop or restart containers to free RAM, complicating resource management in shared environments. The validation logic in [`Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift) enforces minimum memory limits, but cannot prevent cumulative memory bloat across container lifecycles.

## Working With Memory Limits in Practice

Despite these limitations, you can observe and manage memory allocation using the container CLI. The following examples demonstrate how memory limits are applied and why the ballooning behavior requires specific operational workarounds.

Launch a container with an explicit memory limit. The VM allocates up to 8GiB but only uses what the process needs initially:

```bash
container run -d --name myapp --memory 8g myimage:latest

```

Check current memory usage while the container runs:

```bash
container stats myapp

```

The output shows the current consumption against the limit:

```text
MEMORY USAGE / LIMIT   1.3GiB / 8GiB

```

Stop the container and observe that VM memory does **not** shrink:

```bash
container stop myapp

```

Even after stopping the container, Activity Monitor still shows high resident memory for the VM because ballooned pages were never returned to macOS.

Force memory reclamation by restarting the VM:

```bash
container machine stop
container machine start

```

After the restart, the VM's RAM footprint returns to its default baseline, effectively reclaiming the memory that was previously trapped.

## Summary

- **Memory ballooning is unidirectional**: The apple/container implementation only supports memory growth, not shrinkage.
- **Freed pages remain trapped**: Memory released by Linux inside the container stays reserved by the VM and cannot return to macOS.
- **VM restarts are mandatory**: The only method to reduce resident memory footprint is stopping and restarting the VM or container.
- **macOS 26 is required**: Full support requires the latest Apple Silicon release; macOS 15 lacks necessary APIs.
- **Multi-container environments suffer**: Aggregate memory pressure increases without automatic reclamation, forcing manual intervention.

## Frequently Asked Questions

### Can a container VM release memory back to macOS?

No. According to the apple/container source code in [`docs/technical-overview.md`](https://github.com/apple/container/blob/main/docs/technical-overview.md), the macOS Virtualization framework can grow VM memory on demand, but it cannot shrink the VM or return freed pages to the host. Once allocated, memory pages remain reserved for the VM until it is completely restarted.

### Why does my container's memory usage stay high after stopping processes?

Because the implementation lacks host-side reclamation. When Linux inside the container frees memory via standard process termination, those pages stay within the VM's allocation. While `container stats` may show reduced usage inside the guest, the underlying macOS VM retains the physical memory pages indefinitely.

### Which macOS versions support memory ballooning?

Reliable memory ballooning requires macOS 26 (the version shipping with the latest Apple Silicon hardware). On macOS 15, the framework lacks the necessary Virtualization APIs for ballooning support, preventing the feature from working even in its limited capacity as documented in the technical overview.

### How do I reduce memory usage for long-running containers?

You must restart the VM using `container machine stop` followed by `container machine start`, or recreate the container entirely. Consider implementing cron-based restart strategies for fluctuating workloads, or pre-size containers conservatively using the `--memory` flag to prevent excessive host memory consumption.