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 createand store configuration inMachineConfiguration.swiftas JSON snapshots on disk. - User integration is handled by
/etc/machine/create-user.sh, which maps the host's UID/GID and mounts$HOMEfor seamless file access. - Lifecycle management is controlled by
MachinesService.swift, which tracks state and auto-boots stopped machines whenruncommands are issued. - Apple Silicon M3+ supports nested virtualization via
/dev/kvmwhen using the--virtualizationflag 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →