Container Machine vs Regular Container Instance: Key Differences in Apple Container
A container machine is a persistent Linux VM modeled after an entire environment with an init system and user home mapping, while a regular container instance is an ephemeral, single-application process that exits when the command completes.
The apple/container repository provides a lightweight container runtime for macOS that supports two distinct execution models. While both leverage the same underlying virtualization technology, they differ fundamentally in architecture, lifecycle management, and intended use cases. Understanding these distinctions helps developers choose the right abstraction for development workflows versus production workloads.
Architectural Model: Environment vs Application
The primary distinction lies in what each abstraction models.
Container machines are modeled after an entire Linux environment—essentially a full virtual machine with an init system. According to the project documentation in docs/container-machine.md, "Containers are typically modeled after an application. A container machine is modeled after a Linux environment." This means the machine boots like a traditional Linux system, runs /sbin/init, and maintains persistent state across sessions.
Regular container instances are modeled after a single application. The container runs exactly one process (the command you specify) and exits when that process terminates. There is no persistent operating system environment—just the isolated process execution.
Lifecycle and Persistence
Persistence represents the most practical difference between these two approaches.
Container machines operate with a persistent lifecycle. Once created, a machine stays running until you explicitly stop or delete it. The filesystem, installed packages, and user data survive restarts. Data is stored in the machine's own persistent storage on disk, making it suitable for long-running development environments.
Regular container instances are ephemeral by default. They are created, run, and destroyed on demand. When the container stops, its state is discarded unless explicitly committed to an image or saved to external volumes. This makes them ideal for CI jobs, build steps, or microservices that should start fresh each time.
User Experience and File System Mapping
Container machines offer seamless macOS integration that regular containers lack.
In a container machine, the host's macOS username and $HOME directory are automatically mounted inside the Linux VM at /Users/<username>. This enables seamless editing workflows where you modify code on macOS using native editors while building and testing inside the Linux environment. The home directory mapping requires no explicit volume mounts or configuration.
Regular container instances provide no automatic user-home mapping. If you need access to host files, you must explicitly mount volumes using flags like --volume. The container runs in isolation from the macOS host's user directory structure.
Init System and Service Management
The presence of an init system enables fundamentally different capabilities.
Container machines start the image's init system (/sbin/init), allowing you to register and manage long-running services using standard Linux tools like systemctl. You can install daemons, configure autostart services, and manage the system like a traditional Linux server.
Regular container instances have no init system. The container executes only the command you provide and terminates when that command finishes. While you can run background processes, there is no service management framework or proper PID 1 handling.
Resource Allocation and Configuration
Resource management differs significantly between the two models.
Container machines support dynamic resource resizing. You can modify CPU count, memory allocation, and home-mount configuration using the container machine set command. These changes persist to the machine's configuration file (defined in Sources/ContainerPersistence/MachineConfig.swift) and take effect after the next restart.
Regular container instances require resource specification at launch time using flags like --cpus and --memory. These resources cannot be reconfigured without stopping and recreating the container. There is no persistent configuration object for regular containers.
Practical Usage Examples
Working with Container Machines
Create a persistent development environment and manage it like a virtual machine:
# Create a persistent container machine (e.g., an Alpine VM)
container machine create alpine:latest --name dev
# Run commands inside the machine with automatic user/home mapping
container machine run -n dev whoami # prints your macOS username
container machine run -n dev pwd # shows /home/<you> inside the VM
# Open an interactive shell (state persists between sessions)
container machine run -n dev
# Resize resources (applies after next stop/start)
container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run -n dev -- nproc # verifies 4 CPUs
# Delete the machine and all its persistent storage
container machine rm dev
Working with Regular Container Instances
Run isolated, short-lived application workloads:
# Create and run a short-lived container from an image
container run --name web --detach --rm nginx:latest
# Execute a single command inside the container
container exec web uname -a
# The container stops when killed; no persistent state remains
container stop web
Implementation Details
The architectural differences are reflected in the source code implementation:
-
docs/container-machine.md: User-facing documentation explaining the container-machine feature and its Linux environment model. -
Sources/ContainerPersistence/MachineConfig.swift: Defines the on-disk configuration for container machines, including CPU count, memory allocation, and home-mount settings that persist across restarts. -
Sources/ContainerCommands/Container/ProcessUtils.swift: Implements the CLI subcommands that drivecontainer machineoperations (create, run, set, list), handling the persistent VM lifecycle. -
Sources/ContainerXPC/XPCServer.swift: Handles XPC communication, launching a separate helper process for each container machine instance to maintain isolation and persistence. -
Sources/ContainerPlugin/PluginLoader.swift: Manages plugin instances per container machine, ensuring a single instance of each daemon runs within the persistent VM environment.
Regular containers are handled by the generic container runtime code in the same codebase, lacking the persistent configuration and XPC helper process architecture found in the machine implementation.
Summary
- Container machines provide persistent Linux VMs with init systems, automatic macOS home directory mapping, and configurable resources that survive restarts.
- Regular container instances offer ephemeral, single-process isolation without persistence, requiring explicit volume mounts for host file access.
- Source files like
MachineConfig.swiftandProcessUtils.swiftimplement the persistent VM model, while regular containers use the generic runtime. - Use container machines for development workflows requiring stable Linux environments; use regular containers for isolated application workloads and CI jobs.
Frequently Asked Questions
Can I convert a regular container instance into a container machine?
No, you cannot convert between the two. Container machines and regular container instances are fundamentally different architectures—a machine is a persistent VM with an init system, while a regular container is a single ephemeral process. To switch between models, you must create a new container machine from an image and migrate your data manually.
How does the automatic home directory mapping work in container machines?
The container tool automatically mounts your macOS $HOME directory into the Linux VM at /Users/<username> when you run commands inside a machine. This is handled by the XPC server and configuration layer in Sources/ContainerXPC/XPCServer.swift and Sources/ContainerPersistence/MachineConfig.swift, ensuring your development files are accessible without explicit volume flags.
Why would I choose a container machine over a regular container for development?
Choose a container machine when you need a persistent Linux environment that survives reboots, runs multiple services via an init system, and seamlessly integrates with your macOS home directory for editing. Regular containers are better suited for running isolated, stateless applications or one-shot commands that should not persist state between executions.
Do container machines share resources with the host macOS system?
Container machines run in lightweight VMs with dedicated resources. You allocate specific CPU and memory limits using container machine set, and these resources are dedicated to the VM. Unlike regular containers, which share the host kernel, container machines provide stronger isolation through virtualization, though this incurs slightly higher overhead than the lightweight container runtime.
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 →