How to Use Container Machine for VM-Based Workflows with Apple Container

Apple Container provides a container machine abstraction that converts OCI images into persistent, lightweight Linux VMs on macOS, automatically mounting your macOS home directory and preserving state across restarts.

The apple/container repository implements a unique virtualization layer that bridges traditional OCI containers with full Linux virtual machines. This container machine enables you to run long-lived Linux services, develop with native macOS editors while building inside Linux, and even experiment with nested virtualization—all from a standard container image.

What is a Container Machine?

A container machine is a persistent VM managed by the Apple Container runtime that boots from any OCI image containing an init system (/sbin/init). Unlike ephemeral containers, these machines maintain their filesystem across stops and starts, while remaining fully integrated with macOS.

Key characteristics include:

  • Automatic home directory mounting – Your macOS $HOME appears inside the VM at /Users/<username>
  • Persistent state – The VM filesystem survives reboots and is stored in a dedicated machine directory
  • Nested virtualization support – Available on Apple Silicon M3+ with macOS 15+ when using a kernel built with CONFIG_KVM=y
  • Resource flexibility – CPU count, memory limits, and kernel paths are configurable via the MachineConfig API

Core Architecture

The container machine implementation spans several key components in the codebase:

When you execute container machine create, the CLI calls MachineClient.machineConfigFromFlags to resolve image layers and build a boot configuration, which the Machine Service persists to boot-config.json before booting the VM.

Creating and Managing Machines

Quick Start

Create a persistent Linux environment from any OCI image with an init system:


# Create a machine named 'dev' from Alpine

container machine create alpine:latest --name dev

# Open an interactive shell (matches your macOS user)

container machine run -n dev

# Run a single command to verify home directory mounting

container machine run -n dev -- pwd

Set a default machine to omit the -n flag in subsequent commands:

container machine set-default dev
container machine run  # Operates on 'dev' by default

Resource Configuration

Modify VM resources using the set command. Changes persist in MachineConfig and apply after the next stop/start cycle:


# Allocate 4 CPUs and 8GB RAM

container machine set -n dev cpus=4 memory=8G

# Restart to apply changes

container machine stop dev
container machine run -n dev -- nproc  # Verifies 4 CPUs

The configuration is validated via MachineConfig.validateKernelPath when specifying custom kernels, and stored in the machine's boot-config.json.

Advanced Configuration

Enabling Nested Virtualization

For workloads requiring KVM (such as running nested VMs), enable virtualization support when creating the machine:


# Requires Apple Silicon M3+, macOS 15+, and a kernel with CONFIG_KVM=y

container machine create \
    --virtualization \
    --kernel /path/to/vmlinux-kvm \
    --name kvm-dev \
    alpine:latest

# Verify KVM device exposure

container machine run -n kvm-dev -- ls -l /dev/kvm

This configuration sets the nested virtualization flag in MachineConfig and exposes /dev/kvm inside the guest.

Building Custom Machine Images

Create OCI images with systemd for long-running services:

FROM ubuntu:24.04
ENV container container
RUN apt-get update && \
    apt-get install -y dbus systemd openssh-server && \
    apt-get clean && rm -rf /var/lib/apt/lists/* && \
    yes | unminimize
RUN >/etc/machine-id && >/var/lib/dbus/machine-id
RUN systemctl set-default multi-user.target

Build and deploy the custom image:

docker build -t local/ubuntu-machine:latest .

container machine create local/ubuntu-machine:latest --name ubuntu

Lifecycle Management

Manage machine states through the CLI:


# List all machines

container machine ls

# View detailed configuration JSON

container machine inspect dev

# Stop the VM (preserves state)

container machine stop dev

# Delete VM and persistent storage

container machine rm dev

The inspect command returns the decoded MachineConfig including current resource allocations and kernel paths.

Summary

  • Container machines transform OCI images into persistent Linux VMs with seamless macOS home directory integration
  • Configuration persists in boot-config.json via the MachineConfig Codable model, storing CPU, memory, and virtualization flags
  • The Machine Service in MachinesService.swift handles orchestration while the Runtime Service manages actual VM execution
  • Nested virtualization requires Apple Silicon M3+, macOS 15+, and a custom kernel with CONFIG_KVM=y
  • All machine state survives stops and starts, making them suitable for systemd-based workflows

Frequently Asked Questions

What is the difference between a container machine and a standard container?

A container machine is a persistent virtual machine that boots from an OCI image, whereas standard containers share the host kernel and are typically ephemeral. Machines maintain their filesystem across restarts, support init systems like systemd, and provide full Linux kernel isolation through Apple's virtualization framework.

Can I use any Linux OCI image as a container machine?

Any OCI image containing /sbin/init or an alternative init system can function as a machine image. Minimal images without an init process will not boot properly since the VM expects a standard Linux initialization sequence.

Where is the machine configuration stored?

Each machine's configuration is persisted as JSON in a boot-config.json file within the machine's storage directory. This file stores the MachineConfig values including CPU count, memory allocation, home-mount settings, and optional kernel paths, encoded via Swift's Codable protocol.

How does nested virtualization work with container machines?

Nested virtualization exposes the host's ARM virtualization extensions to the guest VM by setting the virtualization flag in MachineConfig. This requires Apple Silicon M3 or later running macOS 15+, along with a Linux kernel compiled with CONFIG_KVM=y. The feature allows running additional hypervisors like QEMU or firecracker inside the container machine.

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 →