# Benefits of Isolated Lightweight VMs Per Container vs Shared Kernels in Apple Container

> Discover the benefits of isolated VMs per container including hardware security, data isolation, and resource control. Learn how Apple's container approach offers advantages over shared kernels.

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

---

**Using isolated lightweight VMs per container delivers hardware-enforced security boundaries, explicit data isolation, and fine-grained resource control compared to shared-kernel approaches, while maintaining startup times comparable to traditional containers.**

The `apple/container` framework implements a unique architecture where each container runs inside its own dedicated lightweight virtual machine rather than sharing a host kernel. This design leverages Apple's Virtualization framework to provide VM-level isolation without the overhead of traditional full virtualization. Understanding these architectural benefits helps developers secure workloads while preserving the fast, lightweight experience expected from modern containerization.

## Architecture Overview

Unlike traditional container runtimes that rely on namespaces and cgroups within a shared kernel, `apple/container` spins up a dedicated **lightweight VM** for every container instance. The `RuntimeService` class, located in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift), manages this lifecycle through an XPC service that creates and configures individual VMs per container.

This approach eliminates the single-kernel attack surface present in shared-kernel containerization. Each container operates within its own kernel environment, ensuring that kernel-level exploits or container escapes cannot affect other containers or the host system.

## Key Benefits of Per-Container VM Isolation

### Security Through Hardware-Enforced Boundaries

Each container runs in its own virtual machine using the `VZVirtualMachine` class, providing isolation guarantees equivalent to full virtualization. The implementation in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift) (lines 32-60) creates a fresh VM instance for every `start(_:reply:)` request, ensuring compromised containers remain trapped within their VM boundaries.

This hardware virtualization approach eliminates the "shared kernel" vulnerability inherent in traditional containerization. While standard containers rely on software-based isolation mechanisms, the per-container VM model leverages CPU virtualization extensions to enforce strict boundaries at the hardware level.

### Explicit Data Isolation and Privacy

Host data mounts into a container **only when explicitly requested**, preventing the common shared-kernel pattern where a single VM must expose a superset of all data that any container might access. Because each container operates in its own VM with independent filesystem views, accidental data leakage between containers is structurally impossible.

This explicit mounting strategy ensures that containers cannot access data belonging to other instances unless specifically configured to do so. The VM boundary acts as a natural privacy barrier, simplifying compliance and data governance requirements.

### Fine-Grained Resource Control

The `ContainerConfiguration` struct in [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift) (lines 150-158) defines per-container resource allocations including `cpus`, `memory`, and `vmOverheadCores`. This structure allows precise resource tuning for individual containers without affecting neighbors or requiring host-wide reservations.

The `vmOverheadCores` parameter specifically reserves CPU capacity for VM-level processes like the guest agent, ensuring that container workloads do not contend with virtualization overhead. This granular control prevents resource exhaustion attacks that can plague shared-kernel environments where containers compete for unpartitioned host resources.

## Implementation Deep Dive

### RuntimeService VM Lifecycle

The `RuntimeService` class implements the VM lifecycle management through its `start(_:reply:)` method. As implemented in the source code:

```swift
public func start(_ request: RuntimeStartRequest, reply: @escaping (RuntimeStartResponse) -> Void) {
    // 1️⃣ Create the VM configuration
    let config = VZVirtualMachineConfiguration()
    // … configure CPU, memory, and attach the Linux boot image …

    // 2️⃣ Instantiate the VM
    self.vm = VZVirtualMachine(configuration: config)

    // 3️⃣ Start the VM
    self.vm?.start(completionHandler: { error in
        if let error = error {
            self.logger.error("Failed to start VM: \(error)")
            reply(RuntimeStartResponse(success: false, error: error.localizedDescription))
            return
        }
        self.logger.info("VM started")
        // Launch the guest agent to manage processes inside the VM
        self.guestAgent = GuestAgent(vm: self.vm!)
        self.guestAgent?.start()
        reply(RuntimeStartResponse(success: true, error: nil))
    })
}

```

This method constructs a `VZVirtualMachineConfiguration`, instantiates the `VZVirtualMachine`, and handles the asynchronous start sequence. The guest agent launches only after successful VM initialization, establishing a secure management channel within the isolated environment.

### ContainerConfiguration Resource Management

Resource allocation is handled through the `ContainerConfiguration` structure:

```swift
public struct ContainerConfiguration {
    /// Number of CPUs allocated to the container.
    public var cpus: Int
    /// Amount of memory allocated to the container.
    public var memory: MemorySize
    /// Additional CPU cores allocated for VM overhead (guest agent, etc).
    public var vmOverheadCores: Int = 1
    // ...
}

```

This configuration ensures that each VM receives dedicated resources while accounting for virtualization overhead separately from container workloads.

## Practical Usage Examples

### Running Containers with Isolated VMs

To launch a container with dedicated resources, use the `container run` command with explicit CPU and memory limits:

```bash

# Allocate 2 CPU cores and 4GB RAM for the container's isolated VM

container run --cpus 2 --memory 4g ghcr.io/apple/containerization/alpine:latest \
    /bin/sh -c "echo Hello from isolated VM"

```

Each execution creates a fresh lightweight VM with the specified resources, providing isolation without manual VM management.

### Managing Dedicated Machine Resources

For persistent VM environments, use the `container machine` command:

```bash
container machine start my-vm \
    --memory 4g \
    --cpus 2 \
    --init-image ghcr.io/apple/containerization/vminit:latest

```

This command creates a named lightweight VM that persists across container runs, allowing you to reuse VM configurations while maintaining per-container isolation boundaries.

## Summary

- **Isolated VMs per container** provide hardware-enforced security boundaries that eliminate shared-kernel vulnerabilities through dedicated `VZVirtualMachine` instances.
- **Explicit data mounting** prevents accidental data leakage between containers by avoiding shared filesystem namespaces and requiring explicit host data access configuration.
- **Fine-grained resource control** via `ContainerConfiguration` allows precise CPU and memory allocation with separate accounting for VM overhead through parameters like `vmOverheadCores`.
- **Fast lifecycle management** through `RuntimeService` creates and starts VMs in seconds, with startup times comparable to traditional containers despite using full virtualization.
- **Source implementation** centers on [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift) and [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift).

## Frequently Asked Questions

### How does per-container VM isolation improve security over shared kernels?

Unlike shared-kernel containers that rely on software namespaces and cgroups, each container in `apple/container` runs inside its own `VZVirtualMachine` instance with a dedicated kernel. This hardware virtualization boundary prevents container escapes from affecting the host or other containers, providing isolation guarantees equivalent to full virtual machines whereas shared-kernel approaches remain vulnerable to kernel privilege escalation exploits.

### What is the performance impact of running a separate VM for each container?

The lightweight VM architecture minimizes overhead by using Apple's Virtualization framework and minimal guest operating systems. While traditional full VMs consume gigabytes of memory, these isolated containers boot in seconds with resource footprints comparable to shared-kernel containers, as evidenced by the minimal `vmOverheadCores` allocation required for guest agent processes.

### Can I control resource allocation for individual containers?

Yes, through the `ContainerConfiguration` struct and CLI flags like `--cpus` and `--memory`. Each container receives its own `VZVirtualMachineConfiguration` with explicit resource limits defined in [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift), preventing resource contention and allowing precise tuning of CPU cores, memory size, and overhead allocation per instance.

### How does data isolation work between container VMs?

Host data mounts explicitly into specific containers rather than sharing a common mount namespace. Because each container runs in its own VM with its own filesystem view via the `VZVirtualMachine` boundary, accidental data exposure between containers is structurally prevented. Data sharing requires explicit configuration, ensuring privacy by default and eliminating the risk of cross-container data leakage common in shared-kernel environments.