How to Run a Linux Container on macOS with Apple's Container Runtime

Apple's container CLI provisions a lightweight Virtual Machine for each Linux container using the native Virtualization framework, enabling you to run Linux workloads on macOS with VM-level isolation and near-native performance.

Apple open-sourced its native container runtime in the apple/container repository, providing a way to run Linux containers on macOS without relying on third-party virtualization layers. This tool leverages Apple's Virtualization and vmnet frameworks to create isolated, per-container VMs that boot a minimal Linux kernel. Here's how to run a Linux container on macOS with Apple's container runtime, from installation to advanced configuration.

Architecture and Components

Core Runtime Components

According to the docs/technical-overview.md file, the runtime consists of several specialized processes that communicate via XPC:

  • container CLI: The front-end binary that parses user commands and communicates with the daemon.
  • container-apiserver: A launch agent started by container system start that implements client-side APIs for container, image, and network management.
  • XPC Helpers: Separate processes owning specific subsystems:
    • container-core-images: Manages the OCI image store and cache
    • container-network-vmnet: Handles virtual network interfaces using the vmnet framework
    • container-runtime-linux: Executes the container runtime inside each VM

The configuration is defined in Sources/ContainerPersistence/ContainerSystemConfig.swift, which loads defaults from ~/.config/container/config.toml.

The Container Lifecycle

When you execute container run, the following occurs as implemented in Sources/ContainerCommands/Container/ContainerRun.swift:

  1. System Initialization: container system start (implemented in Sources/ContainerCommands/System/SystemStart.swift) registers the container-apiserver launch agent and creates the default vmnet network.
  2. VM Provisioning: The CLI requests a new lightweight VM from the daemon, created via the Virtualization framework.
  3. Image Mounting: The VM pulls the OCI image via container-core-images, or uses a local cache.
  4. Container Execution: The container-runtime-linux helper runs the container's PID 1 process inside a Kata-Containers VM.
  5. Resource Attachment: Options like --publish and --volume are translated into virtio-fs mounts and NAT rules on the VM's network interface.

Installation and Setup

Installing the Container Package

Download the signed installer from the GitHub releases page and install it using the macOS installer command:

curl -L -o container.pkg https://github.com/apple/container/releases/latest/download/container-installer-signed.pkg
sudo installer -pkg container.pkg -target /

Starting the Container System

Before running containers, start the background services. This loads the ContainerSystemConfig and initializes the default network:

container system start

This command registers the launch agents and creates the vmnet interface described in docs/technical-overview.md.

Running Linux Containers

Basic Container Execution

Run an interactive Alpine Linux shell using the container run command. This creates a new VM, downloads the image if needed, and executes your command:

container run -it --rm alpine:latest /bin/sh

Inside the container, verify you are running Linux on Apple Silicon:

uname -a

# Output: Linux <uuid> 6.1.68 ... aarch64 GNU/Linux

Networking and Storage

Expose container ports to the host using the --publish flag. The runtime configures NAT rules through the vmnet framework:

container run -d -p 127.0.0.1:8080:80 nginx:latest
curl http://127.0.0.1:8080

Mount host directories into the container using virtio-fs:

container run --volume $HOME/Projects:/src node:latest ls /src

Resource Configuration

Tune VM resources per container using flags that map to the ContainerSystemConfig parameters:

container run --cpus 2 --memory 4g ubuntu:latest

Advanced Configuration

Nested Virtualization and Custom Init

For specialized workloads, you can enable nested virtualization (requires Apple Silicon M3+ and appropriate kernel configuration):

container run --virtualization kvm ubuntu:latest

Run a custom initialization binary before the container starts:

container run --init-image /path/to/vminitd alpine:latest

Network Isolation

Create separate network namespaces for container isolation (requires macOS 26+):

container network create isolated
container run --network isolated alpine:latest

Programmatic Control with Swift

You can interact with the runtime programmatically using the ContainerCommands Swift package. The following example demonstrates starting the system and running a container equivalent to the CLI commands:

import ContainerCommands

// Start the container system programmatically
let system = try await System.start()
print("Container system started: \(system)")

// Load configuration from ~/.config/container/config.toml
let config = try await Application.loadContainerSystemConfig()

// Pull an image via the container-core-images helper
let image = try await Image.pull(
    reference: "alpine:latest",
    containerSystemConfig: config
)

// Run a container with the container-runtime-linux helper
let container = try await Container.run(
    name: "my-shell",
    image: image,
    args: ["/bin/sh"],
    options: .init(interactive: true),
    containerSystemConfig: config
)
print("Container started with ID \(container.id)")

This leverages the ConfigurationLoader.load mechanism and the XPC communication defined in the ContainerAPIService server implementation.

Summary

  • Apple's container runtime uses the native Virtualization framework to provision per-container VMs, providing stronger isolation than traditional container approaches.
  • Installation requires the signed PKG installer from the GitHub releases, followed by container system start to initialize the container-apiserver and vmnet network.
  • Configuration is managed through ContainerSystemConfig in ~/.config/container/config.toml, with per-container overrides for CPU, memory, and networking.
  • Standard OCI workflows work out of the box: container run, container build, and container push operate like their Linux counterparts.
  • Advanced features include nested virtualization on M3+ chips, custom init images, and isolated networks on macOS 26+.

Frequently Asked Questions

How does Apple's container runtime differ from Docker Desktop?

Apple's runtime creates a dedicated lightweight VM for each container using the Virtualization framework, whereas Docker Desktop typically runs a single Linux VM hosting all containers. This architecture provides stronger isolation boundaries and leverages native macOS frameworks like vmnet and launchd rather than relying on a LinuxKit-based VM.

What are the system requirements for running Linux containers?

The runtime requires a Mac with Apple Silicon (ARM64 architecture) and a recent macOS version. Specific features like isolated networks require macOS 26 or later. The container VMs boot a Kata-Containers static kernel (version 6.1.68 as shown in the reference implementation), so your Mac must support the Virtualization framework introduced in macOS 11.

How do I configure default resources for all containers?

Default CPU, memory, and network settings are controlled by the ContainerSystemConfig structure loaded from ~/.config/container/config.toml. As defined in Sources/ContainerPersistence/ContainerSystemConfig.swift, these values serve as defaults for all newly created containers, though you can override them per-container using the --cpus and --memory flags.

Is nested virtualization supported on Apple Silicon?

Yes, but with specific requirements. According to the configuration options in the how-to documentation, you can enable KVM inside containers using the --virtualization flag on Apple Silicon M3 chips or newer. This requires a kernel built with the appropriate virtualization configuration options and only works on newer hardware generations.

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 →