How to Create and Run a Container with `container run` on macOS
container run is the primary command for launching a container from an OCI image on macOS, combining image acquisition, VM creation, and process execution into a single workflow.
The apple/container repository provides a native container runtime for macOS that uses Apple's Virtualization framework to run Linux containers. Understanding how to create and run a container with container run requires familiarity with the command-line interface, the underlying XPC communication architecture, and the resource configuration options available.
Prerequisites: Building or Pulling an Image
Before you can create and run a container, you must have an OCI-compliant image available locally. You can either build a custom image or pull an existing one from a registry.
According to the tutorial documentation in docs/tutorials/start-here.md, the standard workflow begins with building an image:
container build --tag web-test --file Dockerfile .
Alternatively, you can use pre-built images from registries. Once the image is available locally, the container run command can reference it by tag or digest.
The Architecture of container run
When you execute container run, the CLI translates your arguments into a ContainerRuntime request that is sent over XPC to the container-runtime-linux helper. This process involves several key components from the apple/container source code.
Command Parsing and Validation
The container run definition and its options are documented in the command reference at docs/command-reference.md. The CLI validates flags such as --cpus, --memory, and --network before constructing the runtime request.
On macOS 15+, all containers attach to the default vmnet network automatically. If you specify an unsupported network option, the runtime returns an error during the validation phase.
XPC Communication and Runtime Service
The core communication happens in Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift. The RuntimeClient serializes your request into an XPCMessage and sends it to the runtime service.
The runtime service, implemented in Sources/Services/RuntimeLinux/Server/RuntimeService.swift, receives the XPC message and initiates the following sequence:
- Creates a lightweight virtual machine (VZ) using Apple's Virtualization framework
- Configures CPU and memory limits based on the request
- Sets up the root filesystem from the OCI image
- Starts the container process inside the VM
Resource Allocation and Network Defaults
Default values for CPU, memory, and network configuration are stored in Sources/ContainerPersistence/ContainerSystemConfig.swift. When you omit resource flags, the system applies these defaults:
- CPU limits: Mapped to VZ CPU limits via the
--cpusflag - Memory limits: Configured using the
--memoryflag (e.g.,--memory 2G) - Network: Automatically assigned via
vmnetwith an IP address in the 192.168.64.x range
The runtime service tracks the container state (running, stopped, etc.) and implements signals, kill, resize, and cleanup based on flags like --rm and --init.
Step-by-Step Process to Create and Run a Container
The complete workflow to create and run a container involves the following steps, as illustrated in the tutorial at docs/tutorials/start-here.md.
1. Build the Image
Create your application image using the build command:
container build --tag my-app --file Dockerfile .
2. Run the Container
Invoke container run with your desired options. This example runs a detached container with automatic cleanup:
container run --name my-web-server --detach --rm web-test
The --detach flag runs the container in the background, while --rm ensures the container is automatically removed when the process exits. The CLI sends this request via XPC to the runtime service, which creates the VM and starts the process.
3. Verify the Container is Running
List running containers to confirm successful creation:
container ls
The output displays the container ID, image name, OS, architecture, state, and assigned IP address (e.g., 192.168.64.3).
4. Interact with the Container
For interactive processes, use the -it flags:
container run -it my-app /bin/bash
To execute commands in a running container:
container exec my-web-server curl http://192.168.64.3
5. Monitor Resource Usage
Check container statistics with:
container stats --no-stream my-web-server
This displays CPU percentage, memory usage, network I/O, block I/O, and PID count.
6. Stop and Clean Up
If you did not use the --rm flag, stop and remove the container manually:
container stop my-web-server
container rm my-web-server
Practical Code Examples
Here are common patterns for creating and running containers with various configurations:
# Run with an interactive shell
container run -it my-app /bin/bash
# Run detached with port mapping and resource limits
container run -d --name web \
-p 8080:80 \
--memory 2G \
--cpus 2 \
my-app
# Run with an init process to reap zombie processes
container run --init my-app
# Bind-mount a host directory into the container
container run --volume ${HOME}/src:/app \
my-app
# Publish a UNIX socket from host to container
container run --publish-socket /tmp/host.sock:/var/run/container.sock \
my-app
# Run with automatic cleanup and custom name
container run --rm --name temp-test alpine:latest echo "Hello"
Summary
container runis the primary command to create and run a container from an OCI image on macOS, handling VM creation, resource allocation, and process execution.- XPC Communication: The CLI uses
RuntimeClient(Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift) to send requests to the runtime service over XPC. - Resource Management: CPU and memory limits are passed to the VZ virtual machine; defaults are stored in
ContainerSystemConfig.swift. - Networking: On macOS 15+, containers automatically attach to the
vmnetnetwork with assigned IP addresses. - Lifecycle: Use flags like
--rmfor automatic cleanup and--initfor proper process management.
Frequently Asked Questions
What is the difference between container run and container start?
container run both creates and starts a new container from an image in a single command, whereas container start is used to start an existing, stopped container. According to the source in Sources/Services/RuntimeLinux/Server/RuntimeService.swift, container run initiates the full VM creation sequence, while container start resumes a previously configured container state.
How does the container run command communicate with the macOS kernel?
The container run command does not interact directly with the kernel. Instead, it creates a RuntimeClient that sends XPC messages to the container-runtime-linux helper process. This helper, as implemented in RuntimeClient.swift, creates a lightweight Linux virtual machine using Apple's Virtualization framework (VZ), and the container process runs inside that isolated VM environment.
Why does my container get an IP address in the 192.168.64.x range?
On macOS 15 and later, the apple/container runtime automatically attaches all containers to the default vmnet network bridge. This implementation detail, documented in docs/technical-overview.md, provides NAT-based networking where the host can communicate with the container via a predictable IP address range, while the container maintains network isolation from the host's primary interfaces.
What happens if I omit the --memory or --cpus flags when running a container?
If you omit resource allocation flags, the runtime applies default values defined in Sources/ContainerPersistence/ContainerSystemConfig.swift. These defaults ensure that containers receive reasonable baseline resources without exhausting the host system. You can view and modify these defaults through the container configuration system, though explicit flags are recommended for production workloads to ensure predictable performance.
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 →