# How Container Handles Memory Ballooning and When to Restart Containers

> Understand how containers handle memory ballooning on macOS and when a container restart becomes necessary. Learn about dynamic memory adjustment and freeing host resources.

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

---

**Container implements partial memory ballooning through the macOS Virtualization framework, allowing dynamic memory adjustment inside the VM, but requires a container restart to return freed memory to the host macOS system.**

Apple's `container` project runs each workload inside a lightweight Linux virtual machine (VM) created with the macOS Virtualization framework. While this architecture provides strong isolation, it introduces specific constraints around **memory ballooning** that determine how memory is allocated between the container and the host system, and when you must restart containers to reclaim host resources.

## How Memory Ballooning Works in Container

The memory management strategy relies on the Virtualization framework's capabilities, which provide only partial support for traditional ballooning techniques.

### The Virtualization Framework Foundation

When you start a container, `container` creates a dedicated Linux VM using the macOS Virtualization framework. This VM serves as the runtime environment for your containerized processes, isolating them from the host macOS kernel. The framework manages the underlying hardware resources, but its memory reclamation capabilities are limited compared to traditional hypervisors.

### Dynamic Memory Allocation Inside the VM

Memory allocation operates dynamically within the container's VM. When you specify a memory limit using the `--memory` flag (e.g., `--memory 16g`), the VM is configured with that maximum capacity, but it only physically consumes the pages required by the actual workload.

As processes inside the container release memory, the Linux kernel inside the VM frees those pages, causing the VM's resident memory to shrink accordingly. This behavior allows the container to use less than its allocated maximum when the workload is light, providing efficient internal memory management.

### The Host Memory Limitation

Despite the dynamic behavior inside the VM, the macOS Virtualization framework **does not** return freed VM pages back to the host macOS memory pool. According to the [technical overview documentation](https://github.com/apple/container/blob/main/docs/technical-overview.md#releasing-container-memory-to-macos), the host continues to account for the full memory allocation until the VM is destroyed, even though the VM's resident set has shrunk.

## When Containers Need Restart

The limitation in host-side memory reclamation creates scenarios where a **container restart** becomes necessary to free memory back to macOS.

If you run multiple memory-intensive containers sequentially or concurrently, the host can accumulate a large amount of "stuck" memory—allocated to VMs that are no longer actively using those pages. Since the Virtualization framework cannot reclaim this memory while the VM runs, the only way to release it is to destroy the VM and create a fresh one.

This restart process involves stopping the container (which destroys the underlying VM) and starting it again (which creates a new VM with a fresh memory allocation). This is the only method to return reclaimed memory to the macOS host system.

## Configuring Memory Limits

You can control the initial memory allocation using the `--memory` flag when creating or running containers. This value is parsed in [[`Sources/ContainerCommands/Machine/MachineCreate.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Machine/MachineCreate.swift)](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Machine/MachineCreate.swift).

```bash

# Allocate 8 GiB for the container (the VM will only use what the workload needs)

container run --memory 8g myimage:latest

```

The memory size parser supports various units including gigabytes (`g`) and megabytes (`m`), implemented in [[`Sources/ContainerPersistence/MemorySize.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/MemorySize.swift)](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/MemorySize.swift).

## Monitoring Memory Usage

To inspect current memory consumption and identify when a restart might be beneficial, use the `container stats` command. This functionality is implemented in [[`Sources/ContainerCommands/Container/ContainerStats.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerStats.swift)](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerStats.swift).

```bash
container stats <container-id>

# Sample output includes a "Memory Usage" column showing current consumption

```

## Programmatic Memory Configuration

When working with the `container` Swift API, you can programmatically configure memory settings using the `MemorySize` type and default configurations provided by `ContainerSystemConfig`.

```swift
import ContainerPersistence

// Parse a user-provided size string
let size = try MemorySize("4g")          // 4 GiB
print("Bytes:", size.measurement.converted(to: .bytes).value)

// Access the default memory configuration
let defaultMemory = ContainerSystemConfig.defaultMemory
print("Default memory:", defaultMemory)

```

The `MemorySize` implementation handles parsing of size strings like `"4g"` or `"2048mb"`, while `ContainerSystemConfig` provides the `defaultMemory` property for system-wide defaults.

## Summary

- **Partial ballooning**: The macOS Virtualization framework allows dynamic memory adjustment inside the container VM but does not return freed pages to the host.
- **Restart requirement**: Container restart is required to free accumulated memory back to macOS, as stopping destroys the VM and starting recreates it with fresh allocation.
- **Memory limits**: Use the `--memory` flag parsed in [`MachineCreate.swift`](https://github.com/apple/container/blob/main/MachineCreate.swift) to control initial VM memory allocation.
- **Monitoring**: Use `container stats` (implemented in [`ContainerStats.swift`](https://github.com/apple/container/blob/main/ContainerStats.swift)) to track memory usage and determine when restart is needed.
- **API support**: The `MemorySize` type in [`MemorySize.swift`](https://github.com/apple/container/blob/main/MemorySize.swift) and `defaultMemory` in [`ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/ContainerSystemConfig.swift) provide programmatic configuration options.

## Frequently Asked Questions

### Does Container support full memory ballooning?

No, `container` only supports partial memory ballooning. While the Linux VM inside the container can dynamically adjust its memory usage based on workload demands, the macOS Virtualization framework does not support returning freed memory pages back to the host macOS system while the VM is running.

### How do I free memory back to macOS?

You must restart the container by running `container stop <container-id>` followed by `container start <container-id>`. This process destroys the underlying VM and creates a new one, which releases all previously allocated memory back to the host system.

### What is the default memory allocation for containers?

The default memory configuration is defined in `ContainerSystemConfig.defaultMemory` in [[`Sources/ContainerPersistence/ContainerSystemConfig.swift`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift)](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift). You can override this default using the `--memory` flag when running containers.

### How can I monitor container memory usage?

Use the `container stats <container-id>` command to view real-time memory statistics. This command is implemented in [[`Sources/ContainerCommands/Container/ContainerStats.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerStats.swift)](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Container/ContainerStats.swift) and displays current memory consumption, helping you identify when memory pressure necessitates a container restart.