Difference Between Machine and Container Commands in Apple Container

Container machine commands provide a persistent, VM-like Linux environment with init system support and state survival, while regular container commands execute ephemeral, single-process workloads that exit after completion.

The Apple Container project offers two distinct paradigms for running OCI images on macOS. Understanding the difference between machine and container commands is essential for choosing the right tool for development workflows versus isolated tasks. This guide examines the architectural distinction, source implementation, and practical use cases based on the apple/container repository.

Architectural Overview: Machines vs. Containers

A container machine operates as a lightweight VM that boots an OCI image's init system, creating a persistent Linux environment managed by the Container runtime. In contrast, regular container commands launch individual OCI containers that share the runtime but function as ephemeral, single-process execution contexts without persistent state.

Key Differences Between Machine and Container Commands

Purpose and Execution Model

Container machines provide a full Linux host environment capable of running long-lived services like systemd or databases. Regular container commands execute short-lived workloads where the container starts, runs a specific command, then exits.

State Persistence

The machine's root filesystem, user data, and configuration survive reboots and can be resumed later using container machine stop and container machine run. Regular containers are ephemeral by default; stopping destroys runtime state unless explicitly preserved with flags.

User and Home Directory 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 run as root by default and require manual volume mounts (-v or --mount) to share files with the host.

Init System Support

According to the source implementation in Sources/ContainerCommands/Machine/MachineRun.swift, container machines boot the image's init system at /sbin/init, allowing systemd-managed services to function immediately. Regular containers lack an init system; the specified entrypoint becomes PID 1 without service management capabilities.

Resource Configuration

Resource allocation for machines persists across reboots and is configured via container machine set in Sources/ContainerCommands/Machine/MachineSet.swift, affecting CPU, memory, and nested virtualization settings. Regular containers accept resource limits (--cpus, --memory) only for the duration of a single invocation.

Source Code Implementation

The machine abstraction is implemented across several key files in the repository:

Regular container commands follow a different execution path focused on ephemeral OCI container management without the persistence layer defined in MachineConfig.swift.

Practical Usage Examples

Creating and managing a persistent development environment:


# Create a machine named "dev" from Alpine

container machine create alpine:latest --name dev

# Run an interactive shell (user matches host macOS user)

container machine run -n dev

# Execute a command inside the running machine

container machine run -n dev uname -a

Running ephemeral containers for isolated tasks:


# Start a one-off Ubuntu shell

container run -it ubuntu:latest /bin/bash

# Run a detached web server

container run -d --name web -p 8080:80 nginx:latest

Configuring resources:


# Set persistent machine resources (requires restart)

container machine set -n dev cpus=4 memory=8G

# Apply temporary limits to a regular container

container run -it --cpus 2 --memory 2G alpine sh

Managing persistent services with systemd:


# Start PostgreSQL inside a machine

container machine run -n dev -- systemctl start postgresql

# Query the database

container machine run -n dev -- psql -U postgres -c "SELECT version();"

Summary

  • Container machines provide persistent, VM-like Linux environments with init system support and automatic user mapping, ideal for development workflows requiring long-running services.
  • Regular container commands execute ephemeral, single-process workloads without persistent state or automatic home directory mapping, suited for isolated tasks and image building.
  • Machine configuration persists across reboots via MachineConfig.swift, while regular container resources apply only to individual invocations.
  • The machine implementation in Sources/ContainerCommands/Machine/ handles lifecycle management distinct from the ephemeral container runtime.

Frequently Asked Questions

Can I run systemd in a regular container command?

No. Regular container commands execute your specified process as PID 1 without an init system. Only container machines boot /sbin/init, enabling systemd and other service managers to function properly.

Why does my container machine show my macOS home directory?

Container machines automatically map your macOS user's UID/GID and $HOME to /home/<user> in the Linux environment, facilitating seamless file editing between macOS and Linux. Regular containers require manual volume mounts to access host files.

How do I persist changes in a regular container?

Regular containers are ephemeral by design. To preserve changes, commit the container to a new image using container commit or use volumes for data persistence. Alternatively, use a container machine for development scenarios requiring persistent state across sessions.

Can I adjust CPU and memory for both machines and containers?

Yes, but with different persistence models. Use container machine set to permanently configure machine resources in MachineConfig.swift, requiring a stop/start cycle to apply. For regular containers, pass --cpus and --memory flags to container run for temporary, per-execution limits.

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 →