What Are Container Machines and How Do They Operate?

Container machines are persistent Linux environments that run on macOS via lightweight virtualization, bridging the gap between OCI containers and traditional VMs by booting an init system from a standard container image.

Container machines are a core feature of Apple's container platform. They provide fully-featured, persistent Linux environments that survive restarts and can host long-running services, unlike ephemeral OCI containers. According to the apple/container source code, these machines operate by running a virtualized Linux kernel on the host via the built-in Kata Containers runtime, with the container image's init process serving as PID 1.

Architecture and Core Concepts

Container machines occupy a unique middle ground between lightweight containers and traditional virtual machines. Each machine is created from a standard OCI image that contains an init system such as systemd, and stores its own root filesystem on disk for persistence.

When a machine starts, the image's init process becomes PID 1 inside a lightweight virtualized Linux kernel. This architecture allows the machine to run system services (e.g., systemctl start postgresql) while maintaining the portability of container images. The root filesystem persists across reboots, enabling stateful workloads that standard containers cannot support.

Creating Container Machines from OCI Images

The container machine create sub-command instantiates a new machine from any OCI image reference. As documented in docs/container-machine.md, the image must contain /sbin/init to serve as the init system.


# Create a machine from a public image

container machine create alpine:latest --name dev

The command stores a snapshot of the image as the machine's filesystem. This snapshot becomes the persistent root filesystem that survives machine restarts. In Sources/Services/MachineAPIService/Client/MachineConfiguration.swift, all configuration—including CPU count, memory allocation, and mount points—is stored as a JSON snapshot on disk. Changes made via container machine set only take effect after a stop/start cycle, ensuring kernel parameters are applied cleanly.

User Integration and Home Directory Mounting

On first boot, a setup script runs inside the machine to bridge the macOS host and Linux guest environments. Located at /etc/machine/create-user.sh inside the machine image, this script creates a user matching the host's UID and GID.

The host's $HOME directory is mounted at the same path inside the machine (/home/<user>), providing seamless read-write access to files from both sides. This integration allows developers to edit files in macOS while building and running them inside the Linux environment without synchronization delays or permission conflicts.

Lifecycle Management and State Tracking

The MachinesService server handles the full lifecycle of container machines. As implemented in Sources/Services/MachineAPIService/Server/MachinesService.swift, the service tracks each machine's state (running, stopped, etc.) and watches the backing container's exit to update state automatically.

When a container machine run request arrives while the machine is stopped, the service automatically boots it before executing the command. The CLI communicates with this service via MachineClient in Sources/Services/MachineAPIService/Client/MachineClient.swift, which maps operations like create, run, inspect, stop, and set to RPC endpoints. This design allows both the container binary and third-party tools like VS Code Remote-SSH to control machines programmatically.

Nested Virtualization and Custom Kernels

For advanced use cases, container machines support nested virtualization on Apple Silicon M3+ hardware running macOS 15+. When the host supports KVM, you can supply a custom kernel via the --kernel flag and enable the virtualization flag to expose /dev/kvm inside the machine.


# Enable nested virtualization (Apple Silicon M3+)

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

container machine run -n kvm-dev -- ls -l /dev/kvm   # verify /dev/kvm is present

This capability allows developers to run nested virtual machines inside the container machine, making it suitable for kernel development or testing virtualization-dependent workloads.

Practical Usage Examples

The following examples demonstrate common workflows for managing container machines:


# Run a one-off command (auto-boots if needed)

container machine run -n dev whoami      # prints your macOS username

# Open an interactive shell inside the machine

container machine run -n dev            # drops you into /home/<you>

# Set a default machine (so you can omit -n)

container machine set-default dev
container machine run                   # operates on the default

# Adjust resources (applies after next restart)

container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run -n dev nproc     # shows 4 CPUs

The test suite in Tests/CLITests/Subcommands/Machine/TestCLIMachine.swift exercises these exact workflows—creation, boot, run, stop, delete, and error handling—to ensure the CLI and service remain synchronized.

Summary

  • Container machines are persistent Linux environments on macOS that boot from OCI images and run an init system as PID 1 via the Kata Containers runtime.
  • Machines are created using container machine create and store configuration in MachineConfiguration.swift as JSON snapshots on disk.
  • User integration is handled by /etc/machine/create-user.sh, which maps the host's UID/GID and mounts $HOME for seamless file access.
  • Lifecycle management is controlled by MachinesService.swift, which tracks state and auto-boots stopped machines when run commands are issued.
  • Apple Silicon M3+ supports nested virtualization via /dev/kvm when using the --virtualization flag and a custom kernel.
  • All changes to CPU, memory, or other settings require a stop/start cycle to apply to the VM kernel.

Frequently Asked Questions

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

A container machine runs a full Linux kernel with an init system as PID 1 and persists its root filesystem across restarts, behaving like a lightweight VM. Standard Docker containers share the host kernel, run a single process as PID 1, and are ephemeral by design. Container machines can host long-running services like databases with systemd, while Docker containers typically run isolated applications.

How does user mapping work between macOS and the Linux environment?

On first boot, the machine executes /etc/machine/create-user.sh to create a user inside the Linux environment that matches your macOS UID and GID. The script also mounts your macOS home directory at the same path inside the machine (/home/<user>), ensuring file permissions and ownership appear consistent across both operating systems.

Can I run nested virtual machines inside a container machine?

Yes, on Apple Silicon M3+ hardware with macOS 15+, you can enable nested virtualization by creating a machine with the --virtualization flag and supplying a custom KVM-enabled kernel via --kernel. This exposes /dev/kvm inside the machine, allowing you to run nested VMs using hardware acceleration.

Where is container machine configuration stored?

Configuration is persisted as a JSON snapshot on disk by MachineConfiguration.swift. Settings modified with container machine set are written to this file but only take effect after the machine is stopped and restarted, ensuring the virtualized kernel receives the updated parameters during the boot sequence.

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 →