Container Machine vs Regular Container Commands: Architecture and Usage Guide
Container machines provide persistent, VM-like Linux environments with init systems and surviving state, while regular container commands execute ephemeral, single-process workloads that exit after completion.
The Apple Container framework (apple/container) offers two distinct execution models for containerized workloads on macOS. While regular container commands handle transient, single-purpose containers, container machines deliver full Linux environments that bridge the gap between macOS and traditional containerized development.
What Is a Container Machine?
A container machine is a lightweight virtual machine that boots an OCI image with its init system (such as systemd), creating a persistent Linux environment. Unlike ephemeral containers, machines maintain their root filesystem, user data, and configuration across reboots, enabling long-running services and development workflows that require a full Linux host.
According to the source code in Sources/ContainerCommands/Machine/MachineCreate.swift, the container machine create command initializes this VM-like environment, while Sources/ContainerPersistence/MachineConfig.swift defines the persistent configuration structure that stores CPU, memory, and mount settings.
What Are Regular Container Commands?
Regular container commands (such as container run, exec, and build) operate on ephemeral OCI containers designed for short-lived workloads. These containers start a specified process as PID 1, run until completion, and then exit—destroying their runtime state unless explicitly committed to a new image. They share the container runtime but do not provide a persistent OS instance or init system.
Key Architectural Differences
Persistence and State Management
Container machines survive stop/start cycles. You can halt a machine with container machine stop and resume later with container machine run, preserving all installed packages and configuration changes.
Regular containers are ephemeral by default. Stopping a regular container destroys its runtime state, and while the filesystem can be saved as a new image, the container itself cannot be resumed.
Init System and Service Management
Machines boot the image's init system (/sbin/init), making systemd-managed services work out-of-the-box. This enables running databases, web servers, and other background services that require proper signal handling and service management.
Regular containers run the specified entrypoint directly as PID 1 without an init system or service manager. Signals are forwarded to the process, but no PID 1 reaper handles zombie processes automatically.
User Identity and File System Mapping
Machines automatically map the macOS user's UID/GID and $HOME directory into the Linux environment at /home/<user>, enabling seamless file editing between macOS and the Linux environment.
Regular containers default to running as root (or a specified UID via -u), requiring manual volume mounts (-v or --mount) to share files between macOS and the container.
Lifecycle Commands Comparison
Container machines use a dedicated command set implemented across several Swift source files:
- Creation and deletion:
container machine createandcontainer machine delete(implemented inSources/ContainerCommands/Machine/MachineCreate.swift) - Execution:
container machine run(implemented inSources/ContainerCommands/Machine/MachineRun.swift) - Configuration:
container machine setfor persistent resource allocation (implemented inSources/ContainerCommands/Machine/MachineSet.swift) - Inspection:
container machine inspect,list, andlogs
The server-side logic for these operations resides in Sources/Services/MachineAPIService/Server/MachinesService.swift, while the CLI communicates via Sources/Services/MachineAPIService/Client/MachineClient.swift.
Regular container commands follow standard OCI conventions:
container run,exec,start,stop,rmcontainer buildfor image creationcontainer imagemanagement commands
Resource Configuration Models
Machine resources are configured persistently via container machine set, modifying values stored in MachineConfig.swift:
# Configure machine with 4 CPUs and 8GB RAM (effective after restart)
container machine set -n dev cpus=4 memory=8G
Container resources are supplied per-invocation and apply only to that specific execution:
# Launch ephemeral container with limited resources
container run -it --cpus 2 --memory 2G alpine:latest sh
Practical Usage Examples
Creating and Running a Container Machine
# Create a machine named "dev" from Alpine
container machine create alpine:latest --name dev
# Start an interactive shell (user matches macOS host)
container machine run -n dev
# Run a specific command inside the persistent machine
container machine run -n dev uname -a
Running Regular Ephemeral Containers
# One-off Ubuntu container with shell
container run -it ubuntu:latest /bin/bash
# Detached web server container
container run -d --name web -p 8080:80 nginx:latest
Systemd Services in Machines
# Start PostgreSQL inside a machine with systemd
container machine run -n dev -- systemctl start postgresql
# Query the database
container machine run -n dev -- psql -U postgres -c "SELECT version();"
Source Code Architecture
The machine abstraction is implemented through several key components:
Sources/ContainerCommands/Machine/MachineCreate.swift: Handles VM initialization and boot configurationSources/ContainerCommands/Machine/MachineRun.swift: Manages command execution within running machinesSources/ContainerCommands/Machine/MachineSet.swift: Persents configuration changes toMachineConfig.swiftSources/Services/MachineAPIService/Server/MachinesService.swift: Server-side lifecycle management (create, delete, list)Sources/Services/MachineAPIService/Client/MachineClient.swift: Client interface between CLI and machine API
Documentation references include docs/container-machine.md for user-facing concepts and docs/command-reference.md for complete CLI verb listings.
Summary
- Container machines provide persistent, VM-like Linux environments with init system support, surviving reboots, and automatic macOS user/home directory mapping.
- Regular container commands execute ephemeral, single-process containers designed for short-lived workloads, builds, and isolated tool execution.
- Machines boot OCI images with init systems (enabling
systemdservices), while regular containers run entrypoints directly without service management. - Resource configuration for machines persists across restarts via
MachineConfig.swift, whereas regular containers accept resource limits per-invocation only. - Development environments requiring long-running services or full Linux distros should use machines; CI/CD tasks and one-off commands suit regular containers.
Frequently Asked Questions
Can I run systemd services using regular container commands?
No. Regular container commands execute the specified entrypoint directly as PID 1 without an init system. To run systemd services, you must use a container machine, which boots the image's init system (/sbin/init) and provides the service management infrastructure required by tools like systemctl.
How does user mapping differ between machines and regular containers?
Container machines automatically map the macOS user's UID, GID, and $HOME directory into the Linux environment at /home/<user>, enabling seamless file editing across macOS and Linux boundaries. Regular containers default to running as root and require manual volume mounts with explicit UID/GID flags to achieve similar file sharing.
What happens to my data when I stop a machine versus a regular container?
When you stop a container machine using container machine stop, the root filesystem, installed packages, and user data persist and remain available when you subsequently run container machine run. Regular containers lose their runtime state when stopped; only committed layer changes survive, and you cannot resume the same container instance after removal.
Does container machine support nested virtualization?
Yes. Container machines support nested virtualization on Apple Silicon M3 and later processors, configurable via container machine set. This enables running additional virtualization workloads (such as Docker inside the machine or other nested hypervisors) within the persistent Linux environment, a capability not available in regular ephemeral containers.
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 →