How to Create a Container Machine with Apple's Container Runtime
To create a container machine with Apple's container runtime, run container machine create <image> followed by container machine run -n <name> to launch a persistent Linux VM with your macOS home directory automatically mounted.
Apple's open-source container repository provides a native macOS tool for running persistent Linux VMs called container machines. Built from standard OCI images, these machines run via the built-in container runtime on Apple Silicon, automatically mapping your macOS $HOME directory into the VM for seamless cross-platform development. This guide walks through the complete workflow from image selection to advanced configuration using the apple/container source code.
What is a Container Machine?
A container machine is a persistent Linux virtual machine that runs on macOS using the native container runtime. Unlike ephemeral containers, machines maintain state across restarts and provide a full init system (such as systemd) for running background services. According to the apple/container source code, the runtime stores machine configurations in ~/.container/machines and automatically handles VM lifecycle management, including home directory sharing and resource allocation.
Prerequisites
Before creating a container machine, ensure you have:
- The
containertool installed on macOS (Apple Silicon) - The system service running (
container system start) - An OCI-compatible Linux image (any distro shipping
/sbin/init)
Creating Your First Container Machine
The workflow consists of three phases: selecting an image, creating the machine registration, and starting the VM.
Step 1: Select an OCI Image
Choose a standard Linux distribution image from a registry or build a custom one. The image must include an init system (like systemd or OpenRC) to function as a persistent machine.
# Use a lightweight Alpine image for testing
container machine create alpine:latest --name dev
Step 2: Create the Machine
The container machine create command registers the VM in ~/.container/machines and persists the root filesystem. As implemented in Sources/ContainerPersistence/MachineConfig.swift, this stores configuration details including CPU count, memory limits, and virtualization flags.
# Create with explicit resources
container machine create ubuntu:24.04 --name ubuntu-dev --virtualization
Step 3: Start and Use the Machine
Run container machine run to start the VM (if stopped) and open an interactive shell. The runtime automatically mounts the host's $HOME at the same path inside the VM and launches a user session matching your macOS account.
# Open interactive shell
container machine run -n dev
# Execute a single command
container machine run -n dev uname -a
Configuring Container Machine Resources
After creation, tune CPU and memory allocations using the set subcommand. Changes are persisted in Sources/ContainerPersistence/ContainerSystemConfig.swift and apply after the next stop/start cycle.
# Set defaults to avoid typing -n flag
container machine set-default dev
# Adjust resources (takes effect after restart)
container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run # Starts with new resources
Home-directory sharing keeps your macOS files synchronized at /Users/<username> inside the VM, while persistent state ensures changes to the root filesystem survive machine restarts.
Advanced: Custom Images and Nested Virtualization
Building a Custom Image with systemd
For full Linux environment compatibility, build an image containing systemd and essential tools. The examples/container-machine-vscode/Dockerfile in the repository provides a reference implementation.
FROM ubuntu:24.04
ENV container container
RUN apt-get update && \
apt-get install -y dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \
apt-get clean && rm -rf /var/lib/apt/lists/* && \
yes | unminimize
# Prepare machine-id files required by systemd
RUN >/etc/machine-id && >/var/lib/dbus/machine-id
# Systemd defaults
RUN systemctl set-default multi-user.target
RUN systemctl mask dev-hugepages.mount sys-fs-fuse-connections.mount \
systemd-update-utmp.service systemd-tmpfiles-setup.service \
console-getty.service
RUN systemctl disable networkd-dispatcher.service
Build and deploy the custom image:
container build -t local/ubuntu-machine:latest .
container machine create local/ubuntu-machine:latest --name ubuntu
Enabling Nested Virtualization on Apple Silicon
On Apple Silicon M3+ running macOS 15+, enable KVM inside the machine by providing a kernel compiled with CONFIG_KVM=y.
container machine create \
--virtualization \
--kernel /path/to/vmlinux-kvm \
--name kvm-dev \
alpine:latest
# Verify KVM device is exposed
container machine run -n kvm-dev -- ls -l /dev/kvm
Automating User Creation with First-Boot Scripts
Embed a script at /etc/machine/create-user.sh inside your image to control first-boot user setup. The runtime executes this script with environment variables including $CONTAINER_USER.
#!/bin/bash
# /etc/machine/create-user.sh
useradd -m -s /bin/bash "$CONTAINER_USER"
echo "$CONTAINER_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
Understanding the Configuration Architecture
The apple/container runtime persists machine state using Swift structures defined in Sources/ContainerPersistence/MachineConfig.swift, which handles on-disk configuration for CPU, memory, and home-mount settings. Global system settings and the persistent state for all machines are managed by Sources/ContainerPersistence/ContainerSystemConfig.swift. For complete command syntax, refer to docs/command-reference.md and docs/container-machine.md in the repository.
Summary
- Container machines are persistent Linux VMs running via Apple's native container runtime on macOS
- Create machines with
container machine create <image> --name <name>and manage them viacontainer machine run - Configuration persists in
~/.container/machinesand includes CPU, memory, and virtualization flags - Home directory sharing automatically mounts macOS
$HOMEinside the VM at the same path - Nested virtualization requires Apple Silicon M3+ and a kernel with
CONFIG_KVM=y - Custom images need only an init system (
/sbin/init) and optionally/etc/machine/create-user.shfor user setup
Frequently Asked Questions
What is the difference between a container machine and a regular container?
A container machine is a persistent Linux VM with its own init system and state that survives restarts, while regular containers are ephemeral processes. According to the apple/container source code, machines store their root filesystem on disk in ~/.container/machines and boot a full Linux environment, whereas standard containers share the host kernel and disappear when stopped.
Can I use any Docker image as a container machine?
Any OCI-compliant image containing /sbin/init or another init system works as a container machine. Minimal images like alpine:latest work for basic testing, but production use typically requires images with systemd (like Ubuntu or Fedora) to manage background services properly.
How do I enable nested virtualization for running Docker inside a container machine?
On Apple Silicon M3+ (macOS 15+), pass --virtualization and --kernel /path/to/vmlinux-kvm when creating the machine, where the kernel is compiled with CONFIG_KVM=y. This exposes /dev/kvm inside the VM, allowing you to run nested hypervisors or Docker-in-Docker configurations.
Where does the container runtime store machine configuration?
Machine configurations persist in Sources/ContainerPersistence/MachineConfig.swift structures stored on disk at ~/.container/machines. The ContainerSystemConfig.swift file manages global settings and loads the persistent state for all machines, while individual machine metadata includes CPU counts, memory limits, and home-mount paths.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →