📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-07-10 16:04:20 +00:00
parent 2bb0dc8c1d
commit 92c0c3f687
594 changed files with 55224 additions and 19038 deletions
@@ -0,0 +1,165 @@
---
name: apple-container
description: "Build, run, and manage OCI/Linux containers as lightweight per-container VMs on Apple-silicon macOS using Apple's open-source container CLI, no Docker daemon required."
category: devops
risk: critical
source: https://github.com/sanjay3290/ai-skills/tree/main/skills/apple-container
source_repo: sanjay3290/ai-skills
source_type: community
date_added: "2026-07-09"
author: sanjay3290
tags: [macos, containers, oci, apple-silicon]
tools: [claude, cursor, gemini]
license: "Apache-2.0"
license_source: "https://github.com/sanjay3290/ai-skills/blob/main/LICENSE"
---
# Apple `container`
## When to Use
- Use when building, running, or managing OCI/Linux containers on Apple-silicon macOS with Apple's open-source `container` CLI
- Use when you want lightweight per-container VMs instead of a Docker daemon
- Use when translating Docker-style workflows (build, run, exec, logs, networking) to Apple's container tooling
Apple's `container` is an open-source CLI for building, running, and managing OCI/Linux
containers on Apple-silicon Macs. Each container runs inside its own lightweight virtual
machine (backed by the Containerization framework and the Virtualization API), so there is no
shared daemon like Docker — services run per-user via `launchd`. Images are standard OCI
artifacts, so they interoperate with Docker registries and other OCI tooling. The CLI is
deliberately Docker-like (`container run`, `container build`, and image ops under
`container image push`/`pull`), but it is a distinct tool: do not assume Docker command paths,
flags, defaults, or daemon behavior carry over (e.g. there is no `container images`/`push`/`pull`
top-level command — image verbs live under `container image`).
## Safety Gate
Container installation, service startup, image pulls, builds, runs, registry login, pushes,
and resource cleanup change local or remote state. Explain the exact command, image registry,
mounts, ports, privileges, and data-persistence impact, then obtain explicit user approval
before executing it. Do not provide registry credentials, mount sensitive paths, or expose
ports without the user's explicit instruction.
## Requirements
- **Apple silicon only** (M1 or later). Intel Macs are not supported.
- **macOS 26 (Tahoe) is the officially supported target.** The maintainers do not support
older macOS and typically will not fix issues that can't be reproduced on 26. The binary
still runs on **macOS 15 (Sequoia)** but with reduced networking: only the single default
subnet is available, and the `container network` group and `--network` flag error out.
macOS-26-gated features are called out throughout the reference files.
- **Version:** this skill documents the **1.0.0** release (the fullest feature set). The `machine`
group, `container cp`, `container export`, `container prune`, `container image prune`,
`container registry list`, and `container system version` were **added in 1.0.0** (not in 0.7.1)
— features that postdate 0.7.1 are flagged *(1.0.0+)* in the reference files. Run `container --version` and
`container <group> --help` to see what your installed build supports.
- Install by downloading the signed `.pkg` installer from the project's GitHub releases
(`apple/container`) and running it. See `references/concepts.md` for the full
requirements/compatibility matrix and how the VM-per-container model works.
## Setup
Install the signed package, then start the background services once:
1. **Download** the latest signed installer `.pkg` from the
[GitHub releases page](https://github.com/apple/container/releases).
2. **Double-click** the downloaded package and follow the prompts, entering your admin
password so it can place files under `/usr/local`. (There is no documented CLI `installer`
invocation — installation is via the GUI package.)
3. **Start the services** and confirm they are healthy:
```bash
# Start the container services (container-apiserver + helpers via launchd). On first run it
# offers to install the default Linux kernel — accept it, or start non-interactively with
# `--disable-kernel-install` and add a kernel later via `container system kernel set`.
container system start
# Verify services are healthy
container system status
```
`container system start` must have run before any container/image/build command works — a
connection/XPC error almost always means the services are stopped, so run it again. Stop and
deregister the `launchd` services with `container system stop` (which takes only `-p/--prefix`).
The startup flags for `container system start` (`-a/--app-root`, `--install-root`, `--log-root`,
`--enable-kernel-install`/`--disable-kernel-install`, `--timeout`) are in
`references/configuration.md`.
**Upgrade / downgrade / uninstall** use helper scripts in `/usr/local/bin` (stop first with
`container system stop`): `update-container.sh` (add `-v <version>` to pin a version), and
`uninstall-container.sh -d` to remove user data or `-k` to keep it. Full recipes in
`references/workflows.md`.
## Command groups at a glance
Invoke everything as `container <group> <subcommand>`. Container-lifecycle verbs (`run`,
`create`, `start`, `stop`, `exec`, `logs`, `inspect`, `list`/`ls`, `delete`/`rm`, `kill`,
`stats`) and `build` are top-level; image operations like `push`, `pull`, and `tag` live
under `container image`. Run `container <group> --help` for exact flags, or read
`references/commands.md` for the exhaustive matrix.
| Group | What it does | Example |
|-------|--------------|---------|
| container lifecycle | Create, start, run, stop, exec, inspect, list, remove containers | `container run --rm -it docker.io/library/alpine sh` |
| build | Build an OCI image from a Dockerfile in the builder VM | `container build -t myapp:latest .` |
| image | List, tag, inspect, remove, load/save, prune local images; push/pull to registries | `container image ls` |
| registry | Authenticate (login/logout/list) to OCI registries | `container registry login ghcr.io` |
| system | Start/stop/status services, logs, disk usage (`df`), DNS, kernel, properties | `container system status` |
| network | Create/list/remove container networks (**macOS 26 only**) | `container network create mynet` |
| volume | Create/list/inspect/remove persistent volumes | `container volume create data` |
| builder | Manage the builder VM that runs `container build` (start/stop/status) | `container builder status` |
| machine *(1.0.0+)* | Persistent Linux "machine" environments (added in 1.0.0) | `container machine --help` |
Exact subcommand names, aliases, arguments, and flags for each group live in
`references/commands.md` — consult it before running an unfamiliar command rather than
guessing Docker-equivalent syntax.
## Navigating this skill
Read the reference file that matches the task; do not guess flags or behavior.
- **`references/commands.md`** — exhaustive CLI reference: every command group, subcommand,
alias, argument, and flag. Read this to construct any concrete `container ...` invocation,
or to confirm a flag exists before using it.
- **`references/concepts.md`** — architecture (VM-per-container, Containerization framework),
system requirements and macOS 15 vs 26 differences, networking model, per-container IPs,
security model, and a Docker-vs-`container` comparison. Read this to explain how or why
something works, or when a Docker mental model gives the wrong answer.
- **`references/configuration.md`** — the system service, `config.toml` / property model,
default kernel, DNS domains, default registry, builder resources, and machine settings.
Read this to change defaults, tune CPU/memory, point at a private registry, or manage the
kernel.
- **`references/workflows.md`** — copy-pasteable task recipes (run an image, build & push,
wire up local DNS, mount a volume, expose ports) and troubleshooting for common failures.
Read this first when the user wants to accomplish a concrete end-to-end task.
## Key rules
- **This is not Docker.** The CLI resembles Docker, but flags, defaults, and daemon behavior
differ. Verify syntax in `references/commands.md` instead of assuming Docker equivalence.
- **Always ensure services are up first.** Run `container system start` (and confirm with
`container system status`) before any container/image/build command; connection errors
usually mean the services are stopped.
- **Images are standard OCI artifacts** and interoperate with Docker registries and other OCI
tools. Image references that omit a registry default to `docker.io` (configurable via the
`registry.domain` property — see `references/configuration.md`).
- **Each container gets its own IP address** on its network (one lightweight VM per
container). There is no shared Docker bridge; reach a container directly by its IP, or set
up a local DNS domain (`container system dns create ...`, admin required) for name-based
access.
- **`container network` requires macOS 26.** On macOS 15 only the single default subnet is
available and the network command group is unavailable — see `references/concepts.md`.
- **Use fully-qualified image references** when precision matters (e.g.
`docker.io/library/alpine` rather than bare `alpine`) to avoid ambiguity about the source
registry.
## Limitations
- Apple Container requires Apple silicon and has materially different support and networking
behavior across macOS releases; verify the installed CLI version before relying on a flag.
- OCI images and registry content are third-party inputs. Inspect and trust the image source
before pulling or running it.
- This skill does not make container workloads safe by default: mounts, published ports,
privileged settings, registry credentials, and cleanup can expose or destroy data.
- Stop before uninstalling, pruning, deleting containers, volumes, or images, and require
explicit approval for each destructive action.
@@ -0,0 +1,294 @@
# `container` — Concepts & Architecture
Mental model for Apple's `container`: the one-VM-per-container design, the process
topology, platform requirements, networking, images/storage, and the security/isolation
model. Read this to explain *how* or *why* something works, or when a Docker mental model
gives the wrong answer. For concrete flags see `references/commands.md`; for defaults,
`config.toml`, and the machine settings see `references/configuration.md`; for end-to-end
recipes see `references/workflows.md`.
Sources: Apple's `docs/technical-overview.md`, `README.md`, `docs/container-machine.md`,
`SECURITY.md`, and `BUILDING.md`, as of the **1.0.0** release. For behavior on a specific
version, open the matching tag on the
[release page](https://github.com/apple/container/releases).
> **Not Docker.** The CLI is deliberately Docker-like (`container run`, `container build`, image
> ops under `container image`), but `container` is a distinct tool with a different runtime model.
> Do not assume Docker command paths, flags, defaults, or daemon behavior carry over (there is no
> `container images`/`push`/`pull` top-level command) — verify in `references/commands.md`.
---
## 1. What `container` is — one lightweight VM per container
`container` builds and runs standard **OCI/Linux containers** on **Apple-silicon** Macs. It's
written in Swift and built on Apple's open-source
[Containerization](https://github.com/apple/containerization) package for low-level container,
image, and process management.
The defining design choice: **each container you create runs inside its own dedicated
lightweight Linux VM.** This is the opposite of the usual macOS approach (Docker Desktop,
Podman, Lima), where a *single* long-lived Linux VM hosts a daemon and *all* containers share
that one kernel via namespaces.
Each per-container VM boots a **minimal Linux** — a small set of core utilities and dynamic
libraries plus a `vminitd` init — rather than a full userland. Consequences of the model:
| Property | What the per-container VM gives you |
|----------|-------------------------------------|
| **Security** | Every container has the isolation of a *full VM*, not just kernel namespaces. The minimal guest shrinks the attack surface and resource use. |
| **Privacy** | Mount **only the host data a given container needs** into that container's VM. A shared VM forces you to mount everything up front so it can be re-mounted selectively; per-container VMs avoid that broad exposure. |
| **Performance** | Lower memory than a full VM, with **boot times comparable to** containers inside a shared VM (sub-second in practice). |
| **Interop** | Consumes and produces standard **OCI images**, so images move freely to/from Docker registries and other OCI tooling. |
There is **no shared daemon** like `dockerd`. Instead, per-user background services run under
`launchd` and a runtime helper is launched **per container** (see §2).
---
## 2. Architecture — CLI, API server, and helper services
You drive everything through the **`container` CLI**. The CLI uses a **client library**
(`ContainerClient`) that talks over **XPC** to `container-apiserver` and its helpers.
```
container CLI
└── ContainerClient library ──XPC──► container-apiserver (launchd launch agent)
├──► container-core-images ──► local content store
├──► container-network-vmnet ──► virtual network (vmnet)
├──► builder (buildkit) ──► image builds
└──► container-runtime-linux (one instance per container)
└── my-web-server (that container's Linux VM)
```
| Component | Role | Lifecycle |
|-----------|------|-----------|
| `container` CLI + `ContainerClient` | Command entry point; talks to the apiserver over XPC. | Runs per invocation. |
| `container-apiserver` | **launchd launch agent** exposing the client APIs for container and network resources. Launches the helpers below. | Started by `container system start`, torn down by `container system stop`. |
| `container-core-images` | XPC helper exposing the **image-management API**; owns the **local content store**. | Started by the apiserver. |
| `container-network-vmnet` | XPC helper managing the **virtual network** via the vmnet framework; allocates container IPs. | Started by the apiserver. |
| `builder` (`buildkit`) | Utility VM that runs `container build`; communicates over gRPC. | On demand; runs as a container named `buildkit`. |
| `container-runtime-linux` | Container **runtime helper****one instance per container** — exposing the management API for that specific container's VM. | One per running container. |
`container system` is the control plane for this topology: `start`/`stop` bring the
apiserver and helpers up/down, `status` health-checks them, and `logs`/`df`/`kernel`/`dns`/
`property` manage system-wide state. **No container, image, or build command works until
`container system start` has run** — connection errors almost always mean the services are
stopped. See `references/configuration.md` for the full `container system` reference.
launchd service labels (all under the `com.apple.container.` prefix) look like:
```
com.apple.container.apiserver
com.apple.container.container-core-images
com.apple.container.container-network-vmnet.default
com.apple.container.container-runtime-linux.<container-name>
```
macOS frameworks `container` builds on: **Virtualization** (VMs + attached devices),
**vmnet** (virtual network), **XPC** (client↔service IPC), **launchd** (service management),
**Keychain** (registry credentials), and the **unified logging system** (logs, surfaced via
`container system logs`).
---
## 3. Requirements & platform constraints
| Requirement | Detail |
|-------------|--------|
| **Chip** | **Apple silicon only** (M1 or later). Intel Macs are not supported. |
| **macOS** | **macOS 26 is the supported target**`container` relies on new virtualization/networking features there. It **runs on macOS 15** but with the limitations in §7. Maintainers typically will not address issues that can't be reproduced on macOS 26. |
| **Install** | Download the signed `.pkg` from the GitHub release page and run it (installs under `/usr/local`, admin password required). Then `container system start`. |
Some features are **gated on macOS 26** because they depend on vmnet capabilities absent in
macOS 15 (see §4/§7). This gating is enforced at runtime: using a macOS-26-only feature (e.g.
`container network create`, or `--network <name>`) on macOS 15 **errors** rather than
degrading silently.
### macOS 26 vs macOS 15 — feature differences
| Capability | macOS 26 | macOS 15 |
|------------|----------|----------|
| Container-to-container traffic over the virtual network | Works | **Not possible** — vmnet only creates networks where attached containers are isolated from one another. |
| Multiple / custom networks (`container network` group, `--network <name>`) | Available | **Unavailable** — all containers attach to the single `default` vmnet network; `container network …` and `--network` **error**. |
| Container network creation timing | Robust | Network is created only when the **first container starts**; the network helper (which hands out IPs) and vmnet can **disagree on the subnet**, potentially cutting containers off. See "All networking fails on macOS 15" in the upstream `troubleshooting.md`. |
> Build-from-source requirement differs slightly: building needs **macOS 15 minimum, macOS 26
> recommended**, plus **Xcode 26** as the active developer directory (see §6).
---
## 4. Networking model
- Networking is provided by the macOS **vmnet framework**, managed by the
`container-network-vmnet` helper.
- `container system start` creates a vmnet network named **`default`**, typically on CIDR
**192.168.64.0/24** (gateway **192.168.64.1**). Containers attach to `default` unless
`--network` names another.
- **Each container is a first-class network endpoint with its own dedicated IP** on that
network (a direct consequence of the per-container-VM model — there is no shared Docker
bridge). The network helper allocates the IP; read it with `container ls` or
`container inspect <name>` (`.networks[].address`).
- **Reach a container by IP directly.** For name-based access, register a **local DNS domain**
(admin required) so unqualified container names resolve:
```bash
sudo container system dns create test # register domain "test" on the host resolver
# make it the default suffix by editing ~/.config/container/config.toml: [dns] domain = "test"
# (there is NO `property set` CLI — only `container system property list` to view values)
# a container named my-web-server is then reachable as my-web-server.test
```
DNS domain create/delete edits the host resolver configuration, so it **must run as
administrator** (`sudo`). The `dns.domain` property itself is set in `config.toml`, not via a
CLI setter. See `references/configuration.md` for the `dns` commands and the `dns.domain` property.
- **Reach a host service from a container:** create a domain pointed at a host IP with
`--localhost` (e.g. `sudo container system dns create host.container.internal --localhost
203.0.113.113`). Note the macOS caveats: this disables Private Relay, and the packet-filter
rule is dropped on restart.
- **Publish ports to the Mac's loopback** with `--publish [host-ip:]host-port:container-port
[/protocol]`. If a container is on multiple networks, published ports forward to the
interface on the **first** network.
- **Custom / isolated networks require macOS 26.** Create with `container network create
<name> [--subnet … --subnet-v6 …]`. Networks are **mutually isolated** — a container on one
network has no connectivity to containers on another. Default subnets for new networks come
from the `[network]` config (`network.subnet` / `subnetv6`) or are auto-allocated
non-overlapping; the system rejects overlapping custom subnets. Networks support IPv4 and
IPv6. On macOS 15 this whole group is unavailable (§3).
---
## 5. Images & storage
- **Standard OCI in, standard OCI out.** Pull/run images from any OCI registry, and push
images you build to any OCI registry; they run in any other OCI-compatible tool.
- The **local content store** (host-side image/content storage) is owned by the
`container-core-images` helper, which exposes the image-management API. Manage it with the
`images` command group and disk usage with `container system df`.
- **Registries:** image references that omit a host default to **`docker.io`**, configurable
via the `registry.domain` property (see `references/configuration.md`). Prefer
fully-qualified references (`docker.io/library/alpine`, not bare `alpine`) when the source
registry matters. Registry credentials are stored in the macOS **Keychain**.
- **Selective host data sharing.** Only the data a given container needs is mounted into that
container's VM — a privacy/isolation win over the shared-VM model where everything must be
mounted into the one VM up front.
- The application **data root** (default `~/Library/Application Support/com.apple.container/`)
holds `content/`, `containers/`, `images`, `kernels/`, `networks/`, `volumes/`, etc. — see
the data-locations table in `references/configuration.md`.
---
## 6. Security, isolation & building from source
**Isolation model.** Each container is a **full VM**, so isolation is VM-grade rather than
namespace-grade as in a shared-daemon runtime. The minimal guest (small core utility/library
set + `vminitd`) keeps each VM's attack surface and footprint small, and per-container host
mounts limit data exposure to exactly what each container needs.
**Security reporting.** Report vulnerabilities via the project's
[GitHub private vulnerability reporting](https://github.com/apple/container/security/advisories/new),
not public issues. Known/published CVEs may be filed as normal issues. These reports are
**not** eligible for Apple Security Bounties.
**Building from source (brief).** Requires Apple silicon, **macOS 15 min / 26 recommended**,
and **Xcode 26** set as the active developer directory.
```bash
# Build + run tests in an isolated data dir
rm -rf test-data
make APP_ROOT=test-data all test integration
# Install binaries to /usr/local/bin and /usr/local/libexec (admin password)
make install
# Release build (better perf than debug)
BUILD_CONFIGURATION=release make all test integration
BUILD_CONFIGURATION=release make install
```
> **vmnet path bug (macOS 26):** network creation fails if the `container` helper binaries
> live under `~/Documents` or `~/Desktop`. Use `make install` (runs from `/usr/local`), or
> keep the project elsewhere (e.g. `~/projects/container`) when running the `bin`/`libexec`
> build artifacts directly.
To develop against a local checkout of the Containerization package or `container-builder-shim`,
point the runtime config (`~/.config/container/config.toml`) at your local `vminit`/builder
image and restart the services — see `BUILDING.md` for the exact swift-package steps. Attach a
debugger to an XPC helper by exporting `CONTAINER_DEBUG_LAUNCHD_LABEL=<launchd-label>` before
`container system start`.
---
## 7. How it differs from Docker, when to prefer it, and known limits
### `container` vs Docker / shared-VM on macOS
| Aspect | `container` (Apple) | Docker / shared-VM on macOS |
|--------|---------------------|-----------------------------|
| VM topology | **One lightweight Linux VM per container** | One big Linux VM shared by all containers |
| Isolation | Full-VM per container | Namespace isolation within one VM |
| Guest contents | Minimal core utils + libs + `vminitd` | Full Linux userland + daemon |
| Control plane | CLI → `ContainerClient` → `container-apiserver` (launchd) + XPC helpers | `dockerd` daemon inside the shared VM |
| Host data sharing | Mount only what each container needs | Mount everything into the shared VM up front |
| Networking | Per-container dedicated IP on vmnet (`default` ≈ 192.168.64.0/24) | Shared VM networking / Docker bridge |
| Images | Standard **OCI** in/out | OCI |
| Platform | **Apple silicon + macOS 26** (15 with limits) | Cross-platform |
**Prefer `container` when:** you're on Apple silicon and want VM-grade isolation per
container, minimal per-container footprint, per-container IPs, tight macOS integration
(launchd/Keychain/unified logging), and no always-on shared daemon. **Prefer Docker/Podman
when:** you need Intel-Mac support, features `container` hasn't implemented yet, or maximum
Docker-flag/ecosystem compatibility.
### Known limitations
- **Memory is not fully returned to macOS.** The Virtualization framework has only partial
memory ballooning. A VM uses only what the app needs (start with `--memory 16g` but see ~2
GiB in Activity Monitor), but pages freed *inside* the guest are **not** relinquished to the
host. Running many memory-heavy containers may require occasionally **restarting** them.
- **Young project.** 1.0.0 is the first stable release; some containerization features common in
Docker are still unimplemented (check `container <group> --help`). Expect standard semver going
forward — breaking changes on major bumps, not patch releases.
- **macOS 15 networking limits** (see §3): no container-to-container traffic, single `default`
network only, `container network`/`--network` unavailable, and a network-creation race that
can cut containers off.
---
## 8. Container machines — a related but distinct concept
> **⚠️ Requires 1.0.0+.** The `container machine` group was added in 1.0.0 (not in 0.7.1). Check
> `container machine --help` on your install.
`container machine` (alias `m`) is **not** the app-shaped container above: it's a persistent,
integrated **Linux environment** modeled after a distro rather than a single app. It runs the
image's **init system** (so you can register long-running services / a process supervisor),
and **auto-maps your host username and home directory** into the guest, so your repos and
dotfiles are available on both sides with no copy step.
| Trait | Container | Container machine |
|-------|-----------|-------------------|
| Modeled after | one application | a full Linux environment / distro |
| Init | minimal (`vminitd`) | the image's own init (e.g. `systemd`) |
| User | as configured / `root` | matches your **host** account; `$HOME` mounted in |
| Lifetime | ephemeral by task | **persistent** across stop/start |
```bash
container machine create alpine:latest --name dev
container machine run -n dev whoami # your host username, not root
container machine run -n dev # interactive shell; your $HOME is mounted in
container machine set-default dev # then drop -n
container machine set -n dev cpus=4 memory=8G # takes effect after next stop/start
```
- Memory defaults to **half of host memory**; the home mount can be `rw` (default), `ro`, or
`none`. Changes via `container machine set` apply after the next stop/start.
- **Nested virtualization** (`--virtualization` + a custom `CONFIG_KVM=y` kernel via
`--kernel`) needs **Apple silicon M3+ and macOS 15+**; the default kernel does not support
it.
- **Bring your own image:** any Linux image with `/sbin/init` works. On first boot `container`
runs a built-in setup script to provision the mapped user; override it with an executable
`/etc/machine/create-user.sh` in the image (runs once as root with `CONTAINER_UID`,
`CONTAINER_GID`, `CONTAINER_USER`, `CONTAINER_HOME`, `CONTAINER_MACHINE_ID` set).
See `references/commands.md` for the full `machine` subcommand/flag matrix.
@@ -0,0 +1,499 @@
# Configuration & administration
Admin guide for Apple's `container` CLI runtime (Apple silicon macOS). This is **not Docker**
the CLI is Docker-like but the runtime is a per-container lightweight VM. For exhaustive
flags/subcommands see `commands.md`; this file covers the operator surface only.
The runtime is a set of launchd-managed services (`container-apiserver` + machine API server +
background helpers). Almost everything below drives those services. Every command accepts
`--debug` (env `CONTAINER_DEBUG`). Most `list`/`status` commands accept `--format` — commonly
`json`, `table`, `yaml`, and `toml` (the exact value set and default vary by command, e.g. `table`
for `container list` but `toml` for `system property list`; check `<command> --help`).
---
## 1. System service: start / stop / status / logs
The `container` services must be running before any container/image/build command works.
```bash
container system start # register services in launchd, launch apiserver + machine API
container system status # health-check; prints running state + data/install roots
container system stop # stop all services and deregister from launchd
container system logs # recent service logs (default last 5m)
container system logs -f # live tail
container system logs --last 1h
```
`system start` behavior worth knowing:
- Launches `container-apiserver` via launchd, then **pings it** and fails if unresponsive
(bump `--timeout <seconds>` on slow machines).
- On first run it **installs the base filesystem (`vminit`) image** and prompts to install the
**default kernel** unless you pass `--enable-kernel-install` / `--disable-kernel-install`.
- Loads `config.toml` (see §2) once, at startup. **Restart after any config/property change:**
`container system stop && container system start`.
| `system start` flag | Purpose | Default |
|---------------------|---------|---------|
| `-a, --app-root <path>` | Application data root | `~/Library/Application Support/com.apple.container/` |
| `--install-root <path>` | Executables/plugins root | `/usr/local/` |
| `--log-root <path>` | Log dir; unset → macOS unified log | unset |
| `--enable-kernel-install` / `--disable-kernel-install` | Install default kernel or not (mutually exclusive) | prompt |
| `--timeout <seconds>` | Wait for apiserver readiness | XPC default |
`system stop` takes `-p, --prefix <prefix>` (launchd prefix, default `com.apple.container.`);
`system status` takes the same `--prefix` plus `--format`.
`system df` shows reclaimable disk usage for images/containers/volumes:
```bash
container system df --format json
```
**Data locations** (reported by `system status`):
| Location | Default path |
|----------|--------------|
| App data root | `~/Library/Application Support/com.apple.container/` |
| Install root | `/usr/local/` |
| User config | `~/.config/container/config.toml` |
| Package config | `<installRoot>/etc/container/config.toml` (e.g. `/usr/local/etc/...`) |
Data root holds `apiserver/`, `builder/`, `containers/`, `content/`, `kernels/`, `networks/`,
`volumes/`, `snapshots/`, `plugin-state/`, `user-plugins/`, and `state.json`.
---
## 2. System properties / config.toml
One `ContainerSystemConfig` drives the runtime. **Configure it by editing `config.toml`**;
`container system property list` is a read-only view of the effective (merged) values.
- **`config.toml`** section tables (`[build]`, `[container]`, …) are loaded at service startup,
first-match-wins across `~/.config/container/config.toml` then
`<installRoot>/etc/container/config.toml`. Missing keys fall back to hardcoded defaults.
- **Flat property IDs** (e.g. `build.rosetta`, `dns.domain`) are how the same values are named
and reported. `container system property` has only the `list` subcommand — there is **no**
per-key `get`, `set`, or `clear`. To change a value edit `config.toml`; to revert one, remove
its key.
Property ID ⇄ TOML mapping is direct: `build.rosetta``[build] rosetta`, etc. (exceptions:
the builder/vminit image IDs — see table).
```bash
# View effective values (read-only)
container system property list # all IDs, types, current values (default: toml)
container system property list --format json
```
```toml
# Change values by editing ~/.config/container/config.toml, then restart services
[container]
cpus = 8
[registry]
domain = "ghcr.io"
```
```bash
container system stop && container system start # apply; remove a key to revert it
```
**Every configurable key:**
| Property ID | TOML | Type | Default | Meaning |
|-------------|------|------|---------|---------|
| `build.rosetta` | `[build] rosetta` | Bool | `true` | Build amd64 images on arm64 via Rosetta (else QEMU) |
| `build.cpus` | `[build] cpus` | Int | `2` | Builder VM CPU count |
| `build.memory` | `[build] memory` | MemorySize | `"2048mb"` | Builder VM RAM |
| `image.builder` | `[build] image` | String | `ghcr.io/apple/container-builder-shim/builder:<ver>` | Builder (BuildKit) image reference |
| `container.cpus` | `[container] cpus` | Int | `4` | Default CPUs per container (when `run`/`create` omit `--cpus`) |
| `container.memory` | `[container] memory` | MemorySize | `"1g"` | Default RAM per container |
| `dns.domain` | `[dns] domain` | String? | unset | Local domain appended to unqualified container names (§4) |
| `registry.domain` | `[registry] domain` | String | `"docker.io"` | Default registry for bare image refs (§5) |
| `kernel.url` | `[kernel] url` | URL | kata-static release URL | Kernel file or archive to install (§3) |
| `kernel.binaryPath` | `[kernel] binaryPath` | String | `opt/kata/share/kata-containers/vmlinux-<ver>` | Archive-member path of the kernel |
| `network.subnet` | `[network] subnet` | CIDRv4? | unset | Default IPv4 subnet for networks; auto-allocates when unset (§7) |
| — | `[network] subnetv6` | CIDRv6? | unset | Default IPv6 prefix for networks; auto-allocates when unset (§7) |
| `image.init` | `[vminit] image` | String | `ghcr.io/apple/containerization/vminit:<ver>` | `vminitd` image booting container VMs |
| — | `[plugin.<id>]` | — | — | Plugin-scoped config; each plugin reads only its own section |
> Exact defaults (kernel version, image tags) drift per release. Confirm with
> `container system property list`; do not assume a specific version.
**MemorySize** = quoted string, integer + binary unit (`b/kb/mb/gb/tb/pb`, powers of 1024, case-insensitive);
bare integer = bytes. `"2g"` re-emits as `"2gb"`. **CIDR** = quoted, e.g. `"192.168.100.0/24"`.
Minimal `config.toml` example:
```toml
[container]
cpus = 8
memory = "4g"
[dns]
domain = "test"
```
The full per-section `config.toml` schema (keys, types, defaults, meaning), the flat property
list as emitted by the CLI, type-format details, and data locations are in the
[appendix](#appendix-configtoml-schema--data-locations).
---
## 3. Default kernel
The default guest kernel is the Linux kernel every container VM boots. Managed via
`container system kernel set` (writes the default) plus the `kernel.url` / `kernel.binaryPath`
properties (the download source `--recommended` uses).
```bash
container system kernel set --recommended # download + install Apple's recommended kernel
container system kernel set --binary ./vmlinux --force # install a local binary, overwrite
container system kernel set --tar https://ex.com/kata-static.tar.xz \
--binary opt/kata/share/kata-containers/vmlinux-6.12.28-153 --arch arm64
```
| Flag | Purpose | Default |
|------|---------|---------|
| `--recommended` | Download+install recommended kernel. **Overrides `--arch`/`--binary`/`--tar`** | — |
| `--arch <amd64\|arm64>` | Kernel architecture | `arm64` |
| `--binary <path>` | Kernel file, or archive-member name when used with `--tar` | — |
| `--tar <path\|url>` | Tar archive (local or remote) containing the kernel | — |
| `--force` | Overwrite an existing kernel of the same name | — |
- Persist a custom source by editing `config.toml`:
```toml
[kernel]
url = "<url>"
binaryPath = "<member>"
```
- Kernel install is also toggled at startup via `system start --enable/--disable-kernel-install`.
- Per-machine kernel override is separate — see §7 (`machine set kernel=<path>`).
---
## 4. DNS / local domain resolution
Register a **local DNS domain** so unqualified container names resolve host-side (e.g.
`my-web` → `my-web.test`). Creating/deleting a domain **modifies the host resolver and
requires admin** (`sudo`); it restarts `mDNSResponder`.
```bash
sudo container system dns create test # register domain "test"
sudo container system dns create test --localhost 127.0.0.1 # redirect an IPv4 to localhost
container system dns list # list domains (no sudo)
sudo container system dns delete test # alias: rm
```
```toml
# Activate "test" as the default domain in ~/.config/container/config.toml
[dns]
domain = "test"
```
```bash
container system stop && container system start
```
- `--localhost` must be a valid **IPv4** address; it installs a pf redirect rule to `127.0.0.1`.
- Registering a domain is not enough — set `[dns] domain` in `config.toml` to activate it for
unqualified names.
- If domain deactivation misbehaves: `sudo killall -HUP mDNSResponder`.
---
## 5. Default registry & authentication
Bare image refs resolve against the default registry (`registry.domain`, default `docker.io` →
`alpine` becomes `docker.io/library/alpine`).
```toml
# Change the default registry in ~/.config/container/config.toml (restart to apply);
# remove the key to revert to docker.io
[registry]
domain = "ghcr.io"
```
**Auth** (`container registry`, alias `r`) — credentials are stored in the macOS keychain:
```bash
container registry login registry.example.com # prompts user + password
container registry login -u alice ghcr.io # prompt password only
echo "$TOKEN" | container registry login -u alice --password-stdin ghcr.io # non-interactive
container registry list # HOSTNAME/USERNAME/MODIFIED/CREATED (1.0.0+)
container registry logout ghcr.io
```
| Command | Key flags | Notes |
|---------|-----------|-------|
| `login <server>` | `-u/--username`, `--password-stdin`, `--scheme <http\|https\|auto>` | `--password-stdin` **requires** `--username`. On success pings the registry then saves to keychain |
| `logout <registry>` | — | Deletes keychain creds for the resolved host |
| `list` (`ls`) | `--format`, `-q/--quiet` | `-q` prints hostnames only |
**Insecure / HTTP registries** — `--scheme` (default `auto`):
- `auto` → `http` for internal/private hosts (`localhost`, the internal DNS domain, and RFC-1918
ranges `10/8`, `127/8`, `192.168/16`, `172.16/12`); `https` for everything else.
- Force plaintext against a local registry: `container registry login --scheme http localhost:5000`.
- `--scheme` also applies to image `pull`/`push` and `machine create` (same enum).
> `registry list` only lists saved logins. There is no `default-registry get/set` subcommand —
> the default registry lives in the `registry.domain` property (above).
---
## 6. BuildKit builder VM
`container build` runs inside a utility container named **`buildkit`** (a lightweight VM). It
auto-starts on first build; manage it explicitly with `container builder` when you need more
resources or a clean slate.
```bash
container builder start --cpus 8 --memory 32g # start with more resources
container builder status # ID/IMAGE/STATE/IP/CPUS/MEMORY
container builder stop
container builder delete # alias: rm (add --force if running)
```
To resize a running builder, recreate it:
```bash
container builder stop && container builder delete && container builder start --cpus 8 --memory 32g
```
| Command | Flags | Notes |
|---------|-------|-------|
| `start` | `-c/--cpus <n>`, `-m/--memory <size>` (K/M/G/T/P suffix) | Falls back to `build.cpus`/`build.memory` (defaults 2 CPUs / 2048 MiB). Reuses an existing matching builder; recreates if image/cpu/mem/env/DNS changed |
| `status` | `--format`, `-q/--quiet` | Prints `builder is not running` when absent |
| `stop` | — | No-op warning if not running |
| `delete` (`rm`) | `-f/--force` | Errors unless stopped; `--force` stops then deletes |
- Image comes from `image.builder` / `[build] image`; Rosetta-vs-QEMU for amd64 builds is
`build.rosetta`.
- Persistent builder defaults (not just this session) go in `[build] cpus` / `[build] memory`
in `config.toml`.
---
## 7. Machines, custom networks, volumes
### Machines (`container machine`, alias `m`)
> **⚠️ Requires 1.0.0+ — the `container machine` group was added in 1.0.0 (not in 0.7.1).**
A **container machine** is a persistent, bootable Linux **environment** (not a one-shot
container) — it runs the image's init system, auto-mounts your macOS `$HOME` and username, and
persists across restarts. Ideal for "edit on Mac, build inside Linux" and running long-lived
services. Distinct from the transient per-container VMs that back `container run`.
```bash
container machine create alpine:3.22 --name dev --set-default
container machine run # interactive shell in the default machine (matches host user)
container machine run -n dev uname
container machine run -n dev -- cat /proc/cpuinfo # everything after -- passed through verbatim
container machine list # ls: NAME/CREATED/IP/CPUS/MEMORY/DISK/STATE/DEFAULT(*)
container machine inspect dev # JSON detail
container machine set -n dev cpus=4 memory=8G home-mount=ro # takes effect after stop/start
container machine set-default dev
container machine stop dev
container machine delete dev # rm: also deletes persistent storage
container machine logs -f -n 100 dev # --boot for boot log
```
| Subcommand | Key flags / settable keys | Notes |
|------------|---------------------------|-------|
| `create <image>` | `-n/--name`, `--set-default`, `--no-boot`, `--cpus`, `--memory`, `--home-mount <ro\|rw\|none>`, `--virtualization`, `--kernel <path>` | Memory default = half host RAM; home-mount default `rw`. Name defaults to `<image>-<tag>` |
| `set [-n]` | `cpus=`, `memory=`, `home-mount=`, `virtualization=<bool>`, `kernel=<path>` (empty `kernel=` resets to default) | `key=value`; last dupe wins; unknown key errors. Changes apply after stop/start |
| `run` | `-d/--detach`, `--root`, `-e/--env`, `-i`, `-t`, `-u/--user`, `-w/--workdir`, `--ulimit` | Boots machine if stopped; forwards `SSH_AUTH_SOCK` |
| `logs` | `--boot`, `-f/--follow`, `-n <lines>` | `-n` is short-only |
| `list`/`inspect`/`stop`/`delete`(`rm`)/`set-default` | ID optional (uses default) except `set-default`/`delete` | — |
**Nested virtualization / custom kernel** (per-machine): requires Apple Silicon **M3+** on
**macOS 15+** and a kernel with `CONFIG_KVM=y` (the default kernel does not qualify).
```bash
container machine create --virtualization --kernel /path/to/vmlinux-kvm -n kvm-dev alpine:latest
container machine run -n kvm-dev -- ls -l /dev/kvm # verify /dev/kvm exposed
```
Any Linux image with `/sbin/init` works. Override first-boot user provisioning by adding an
executable `/etc/machine/create-user.sh` (runs once as root; env `CONTAINER_{UID,GID,USER,HOME,MACHINE_ID}`).
### Custom networks (`container network`, alias `n`) — **macOS 26+**
`system start` always creates a builtin vmnet network `default`. On **macOS 26+** you can create
additional **isolated** networks (no cross-network connectivity). Builtin networks cannot be deleted.
```bash
container network create foo # prints network ID
container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
container network list # ls: NETWORK/SUBNET
container network inspect foo # pretty JSON
container run -d --name web --network foo web-test # attach a container
container network delete foo # rm; add --all for all non-builtin
container network prune # remove networks with no attached containers
```
| Flag (`create`) | Purpose | Default |
|-----------------|---------|---------|
| `--subnet <cidr4>` / `--subnet-v6 <cidr6>` | Custom subnets (validated non-overlapping) | auto-allocate / `network.subnet(v6)` |
| `--internal` | Host-only network (no NAT) | NAT |
| `--label k=v` / `--option k=v` | Metadata / plugin option (repeatable) | — |
| `--plugin <name>` | Network plugin | `container-network-vmnet` |
`delete --all` skips builtin networks; deleting a network fails if any IP is still in use.
Persistent default subnets → `network.subnet` / `[network] subnetv6`. Their effect is
version-dependent: on **macOS 15** they set the subnet of the single builtin `default` network;
on **macOS 26+** they additionally seed the default subnet for custom networks created without an
explicit `--subnet`/`--subnet-v6`. In both cases, if left unset the system auto-allocates a
non-overlapping subnet.
### Volumes (`container volume`, alias `v`)
A **volume** is host-backed storage you mount into containers to persist/share data across runs
(`container run --volume <name>:/path` or a host `path:path`). Driver is always `local`.
```bash
container volume create mydata -s 10G # prints volume name (-s takes K/M/G/T/P)
container volume create mydata --label env=dev --opt key=val
container volume list # ls: NAME/TYPE/DRIVER/OPTIONS
container volume inspect mydata # pretty JSON
container volume delete mydata # rm; --all for all volumes
container volume prune # remove volumes with no container refs; reports reclaimed space
```
| Flag (`create`) | Purpose |
|-----------------|---------|
| `-s <size>` | Size in bytes, K/M/G/T/P suffix (stored as driver opt `size`) |
| `--label k=v` | Metadata (repeatable) |
| `--opt k=v` | Driver-specific option (repeatable) |
`delete`/`network delete` require either names **or** `--all` (not both); a missing named
resource errors, while `--all` tolerates an empty set.
---
## Appendix: `config.toml` schema & data locations
One underlying `ContainerSystemConfig` is configured by editing a **`config.toml`** file with
`[section]` tables loaded once at service startup. The same values are also named as flat
**property IDs** (`build.rosetta`, `dns.domain`, …) — view them read-only with
`container system property list` (its only subcommand; there is no `get`/`set`/`clear`). Property
IDs map onto TOML sections directly (`build.rosetta` ⇄ `[build] rosetta`), with two naming
exceptions: the builder image is `image.builder` ⇄ `[build] image`, and the vminitd image is
`image.init` ⇄ `[vminit] image`. For the CLI commands themselves see `commands.md`.
### Sources & precedence
TOML is loaded **first-match-wins**, then any key absent from both files falls back to a
hardcoded default:
1. User file — `~/.config/container/config.toml`
2. Package file (optional) — `<installRoot>/etc/container/config.toml`
(e.g. `/usr/local/etc/container/config.toml`)
The file is read **once at startup**; restart after edits
(`container system stop && container system start`).
### Top-level sections
```toml
[build] # builder VM resources and image
[container] # default per-container resources
[dns] # default DNS domain for host DNS resolution
[kernel] # guest kernel binary path and download URL
[network] # default subnets for networks
[registry] # default registry domain
[vminit] # default vminitd image
[plugin.<id>] # zero or more plugin-scoped sections
```
All sections are optional; an omitted section falls back to its defaults wholesale. (The Swift
source also defines an internal `[machine]` section, not part of the documented user schema.)
### `[build]` — builder VM (runs `container build`)
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `rosetta` | `build.rosetta` | Bool | `true` | Build amd64 images on arm64 using Rosetta translation, instead of QEMU. |
| `cpus` | `build.cpus` | Int | `2` | CPU count for the builder VM. |
| `memory` | `build.memory` | MemorySize | `"2048mb"` | RAM allocation for the builder VM. |
| `image` | `image.builder` | String | `ghcr.io/apple/container-builder-shim/builder:<tag>` | Reference for the builder image; `<tag>` tracks the bundled `container-builder-shim` version. (Property ID is `image.builder`, not `build.image` — see the naming exception above.) |
### `[container]` — default per-container resources
Applied when `container run` / `container create` runs without `--cpus` / `--memory`.
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `cpus` | `container.cpus` | Int | `4` | Default CPU count per container. |
| `memory` | `container.memory` | MemorySize | `"1g"` | Default RAM per container. |
### `[dns]` — local DNS domain
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `domain` | `dns.domain` | String? | unset | Local DNS domain appended to unqualified container hostnames. `"test"` makes `my-web` resolvable as `my-web.test`. Unset → no domain appended. See §4 for the register-then-activate workflow. |
### `[kernel]` — default guest kernel
Defaults change per release as kernels are bumped; confirm with
`container system property list`.
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `binaryPath` | `kernel.binaryPath` | String | `opt/kata/share/kata-containers/vmlinux-<ver>` | Archive-member pathname of the kernel, when the URL points at an archive. |
| `url` | `kernel.url` | URL | kata-static release `.tar.xz`/`.tar.zst` | URL of the kernel file to install, or of an archive containing it. |
### `[network]` — default subnets
Used when creating networks without explicit `--subnet` / `--subnet-v6`. See §7 for the
macOS 15 vs macOS 26+ behavior.
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `subnet` | `network.subnet` | CIDRv4? | unset | IPv4 CIDR, e.g. `"192.168.100.0/24"`. Unset → system auto-allocates a non-overlapping subnet. |
| `subnetv6` | — | CIDRv6? | unset | IPv6 CIDR, e.g. `"fd00:abcd::/64"`. Unset → system auto-allocates. |
### `[registry]` — default registry
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `domain` | `registry.domain` | String | `"docker.io"` | Default registry for image references that omit a registry host (`alpine` → `docker.io/library/alpine`). See §5. |
### `[vminit]` — default vminitd image
| Key | Property ID | Type | Default | Description |
|-----|-------------|------|---------|-------------|
| `image` | `image.init` | String | `ghcr.io/apple/containerization/vminit:<tag>` | Reference for the `vminitd` image used to boot container VMs; `<tag>` tracks the bundled `containerization` version. |
### `[plugin.<id>]` — plugin-scoped config
Plugins ship their own schemas under `[plugin.<id>]` (`<id>` = plugin identifier). Each plugin
defines and reads only its own section; values cannot leak across plugins. Consult the specific
plugin's documentation for its keys.
### Type formats
**MemorySize** — a quoted string: numeric prefix + binary unit suffix (case-insensitive). All
units are **binary** (powers of 1024) even when written `kb`/`mb`/`gb`. A bare integer (`"2048"`)
parses as bytes. Encoded form uses lowercase `b`/`kb`/`mb`/`gb`/`tb`/`pb` (so `"2g"` re-emits as
`"2gb"`).
| Suffix family | Unit | Examples |
|---------------|------|----------|
| `b` | bytes | `"1024b"` |
| `k`, `kb`, `kib` | kibibytes (1024 B) | `"512k"`, `"512kb"` |
| `m`, `mb`, `mib` | mebibytes | `"2048mb"` |
| `g`, `gb`, `gib` | gibibytes | `"4g"`, `"4gb"` |
| `t`, `tb`, `tib` | tebibytes | `"1t"` |
| `p`, `pb`, `pib` | pebibytes | `"1p"` |
**CIDRv4 / CIDRv6** — quoted strings, e.g. `"192.168.100.0/24"` and `"fd00:abcd::/64"`. Invalid
CIDR strings are rejected at decode time.
### Data locations
Reported by `container system status`:
| Location | Path (default) | Overridable with |
|----------|----------------|------------------|
| Application data root | `~/Library/Application Support/com.apple.container/` | `container system start --app-root <path>` |
| Application install root | `/usr/local/` | `container system start --install-root <path>` |
| Log root | macOS unified log facility (if unset) | `container system start --log-root <path>` |
| User config file | `~/.config/container/config.toml` | — |
| Package config file | `<installRoot>/etc/container/config.toml` (e.g. `/usr/local/etc/container/config.toml`) | — |
The data root contains subdirectories such as `apiserver/`, `builder/`, `containers/`,
`content/`, `kernels/`, `networks/`, `plugin-state/`, `snapshots/`, `user-plugins/`, `volumes/`,
and a top-level `state.json`.
@@ -0,0 +1,498 @@
# `container` — Task-Oriented Workflows
Copy-pasteable recipes for Apple's `container` CLI (`apple/container`) — Linux containers as
lightweight VMs on Apple-silicon macOS. This is **Apple's `container`, not Docker**: flags,
defaults, and the daemon model differ. See `commands.md` for the full flag reference. Use
fully-qualified image refs (`docker.io/library/alpine`) in scripts. `container` targets
**macOS 26**; several features are gated (called out inline).
> **Version:** recipes track the **1.0.0** release. `container cp`, `container prune`,
> `container image prune`, and `container system version` used below were added in 1.0.0 (not in
> 0.7.1) — run `container --help` to confirm your installed surface.
## Recipes
- [First run: install + start + first container](#first-run-install--start--first-container)
- [Run interactive vs detached](#run-interactive-vs-detached)
- [Publish ports](#publish-ports)
- [Set environment variables](#set-environment-variables)
- [Mounts: bind, volume, tmpfs](#mounts-bind-volume-tmpfs)
- [Build an image from a Dockerfile](#build-an-image-from-a-dockerfile)
- [Multi-tag, build-args, no-cache, target](#multi-tag-build-args-no-cache-target)
- [Tag & push to Docker Hub / private registry](#tag--push-to-docker-hub--private-registry)
- [Set the default registry](#set-the-default-registry)
- [Container-to-container networking](#container-to-container-networking)
- [Local DNS domain for name resolution](#local-dns-domain-for-name-resolution)
- [Custom isolated networks (macOS 26)](#custom-isolated-networks-macos-26)
- [Inspect a container](#inspect-a-container)
- [View logs](#view-logs)
- [Exec into a running container](#exec-into-a-running-container)
- [Copy files in and out](#copy-files-in-and-out)
- [Resource limits (CPU / memory)](#resource-limits-cpu--memory)
- [Cleanup: stop, remove, prune](#cleanup-stop-remove-prune)
- [Upgrade / downgrade / uninstall](#upgrade--downgrade--uninstall)
- [Troubleshooting](#troubleshooting)
---
### First run: install + start + first container
There is **no CLI installer command** — download and double-click the signed `.pkg`, then start
the services and run a container.
```bash
# 1. Download the signed installer .pkg from the GitHub releases page:
# https://github.com/apple/container/releases
# Double-click the .pkg and follow the prompts (admin password installs under /usr/local).
# 2. Start the background services. On first run this prompts to install the default Linux kernel.
container system start
# 3. Verify (empty list is expected on a fresh install).
container list --all
# 4. Run your first container.
container run --rm -it docker.io/library/alpine:latest sh
```
Note: double-clicking the `.pkg` is the only install path; `container system start` prompts for the recommended kernel (or use `container system kernel set --recommended`).
---
### Run interactive vs detached
```bash
# Interactive shell (-i keeps stdin open, -t allocates a TTY; commonly combined as -it).
container run --rm -it docker.io/library/ubuntu:latest /bin/bash
# Detached, named, auto-removed on stop.
container run -d --name web --rm docker.io/library/nginx:latest
# One-shot command in the foreground (exits when the command finishes).
container run --rm docker.io/library/alpine:latest uname -a
```
Note: foreground by default; stdin stays closed unless `-i`. `--rm` deletes the container after it exits.
---
### Publish ports
`-p` / `--publish` forwards loopback traffic to the container. Spec: `[host-ip:]host-port:container-port[/protocol]` (`tcp`/`udp`, case-insensitive).
```bash
# host 8080 -> container 80
container run -d --rm -p 8080:80 docker.io/library/nginx:latest
# bind to a specific host IP
container run -d --rm -p 127.0.0.1:8080:8000 docker.io/library/node:latest npx http-server -a :: -p 8000
# IPv6 loopback (quote the bracketed address)
container run -d --rm -p '[::1]:8080:8000' docker.io/library/node:latest npx http-server -a :: -p 8000
# UDP
container run -d --rm -p 5353:5353/udp docker.io/library/alpine:latest
```
Note: if a container attaches to multiple networks, published ports forward to the IP of the first network's interface.
---
### Set environment variables
```bash
# Individual vars (-e / --env); repeatable. Bare `KEY` inherits from the host.
container run --rm -e NODE_ENV=production -e PORT=8080 docker.io/library/node:latest env
# Inherit HOME from the host shell
container run --rm -e HOME docker.io/library/alpine:latest sh -c 'echo $HOME'
# From a file (KEY=value per line; # comments and blanks ignored); repeatable.
container run --rm --env-file ./app.env docker.io/library/alpine:latest env
```
Note: `-e` and `--env-file` may be combined and each repeated; later values win.
---
### Mounts: bind, volume, tmpfs
```bash
# Bind-mount a host folder (-v host:container). Use absolute host paths.
container run --rm -v "${HOME}/Desktop/assets:/content/assets" docker.io/library/python:alpine ls -l /content/assets
# Same via --mount key=value syntax.
container run --rm --mount source="${HOME}/Desktop/assets",target=/content/assets docker.io/library/python:alpine ls /content/assets
# Named volume (create first, then mount by name).
container volume create data
container run --rm -v data:/var/lib/app docker.io/library/alpine:latest sh
# Anonymous volume (-v /path) — NOT auto-removed by --rm; delete it yourself.
container run --rm -v /data docker.io/library/alpine:latest sh
# It leaves an anon-<id> volume. List to find the exact name, then remove THAT one by name:
container volume list # locate the new anon-<id> row
container volume rm anon-<id> # ⚠️ delete the specific volume — do NOT `grep anon | rm`, which
# would match (and destroy) every anonymous volume on the machine
# Read-only bind via --mount.
container run --rm --mount type=bind,source="${PWD}/conf",target=/etc/conf,readonly docker.io/library/alpine:latest cat /etc/conf/app.ini
# tmpfs (in-memory) mount; repeatable.
container run --rm --tmpfs /scratch docker.io/library/alpine:latest sh -c 'df -h /scratch'
```
Note: unlike Docker, anonymous volumes do not auto-clean with `--rm` — remove them via `container volume rm`.
---
### Build an image from a Dockerfile
```bash
# Build from ./Dockerfile in the current context, tag as web-test.
container build -t web-test .
# Explicit Dockerfile path.
container build -f docker/Dockerfile.prod -t web-test:prod .
# Build, then run it.
container build -t web-test .
container run -d --name my-web-server --rm web-test
```
Note: with no `-f`, the builder looks for `Dockerfile` then `Containerfile`. The builder runs in its own VM (see resource-limits recipe to size it).
---
### Multi-tag, build-args, no-cache, target
```bash
# Multiple tags on one build (-t repeatable).
container build -t my-app:latest -t my-app:v1.0.0 .
# Build-time variables (repeatable).
container build --build-arg NODE_VERSION=18 --build-arg ENV=prod -t my-app .
# Skip the build cache.
container build --no-cache -t my-app .
# Target a specific multi-stage build stage.
container build --target production -t my-app:prod .
# Multi-architecture image (arm64 + amd64 via Rosetta).
container build --arch arm64 --arch amd64 -t docker.io/me/web-test:latest .
```
Note: `--arch` is repeatable/comma-separated; `--platform` (`os/arch[/variant]`) takes precedence over `--os`/`--arch`.
---
### Tag & push to Docker Hub / private registry
Registry commands live under the **Image** group (`container registry`, alias `r`). Credentials are stored in the macOS keychain.
```bash
# Log in (prompts for username + password, or use -u / --password-stdin).
container registry login docker.io
container registry login -u alice ghcr.io
echo "$TOKEN" | container registry login -u alice --password-stdin ghcr.io
# HTTP for a local registry.
container registry login --scheme http localhost:5000
# Tag the local image with a full registry reference.
container image tag web-test docker.io/alice/web-test:latest
# Push it (the final reference is printed on success).
container image push docker.io/alice/web-test:latest
# Private registry.
container image tag web-test registry.example.com/fido/web-test:latest
container image push registry.example.com/fido/web-test:latest
# List saved logins / log out.
container registry list # 1.0.0+ (added after 0.7.1)
container registry logout ghcr.io
```
Note: the default registry is `docker.io`, so a bare `web-test:latest` resolves against Docker Hub. Change it via the `registry.domain` property (next recipe).
---
### Set the default registry
```bash
# Inspect current properties (includes [registry] domain).
container system property list
```
Edit `~/.config/container/config.toml`:
```toml
[registry]
domain = "registry.example.com"
```
Note: with `registry.domain` set, unqualified image names resolve against that host instead of `docker.io`.
---
### Container-to-container networking
Each container gets its own IP on the `default` vmnet network. Read it from `container ls` or `container inspect`.
```bash
# Start a server, then find its IP.
container run -d --name my-web-server --rm web-test
container ls # IP column shows e.g. 192.168.64.3
# Scriptable IP lookup via inspect + jq.
container inspect my-web-server | jq -r '.[0].networks[0].address'
# Reach it from another container by IP (requires macOS 26 — see note).
container run --rm -it web-test curl http://192.168.64.3
```
Note: container-to-container access over the virtual network requires macOS 26 and **does not work on macOS 15**, where containers are isolated from one another at the network layer (see `concepts.md` §3) — a naming scheme cannot restore connectivity. The DNS domain in the next recipe resolves names **host-side only** (host → container); it does not enable container-to-container traffic on macOS 15.
---
### Local DNS domain for name resolution
Register a local domain so `<name>.<domain>` resolves to a named container's IP. `dns create`/`delete` need `sudo` (writes under `/etc/resolver`).
```bash
# Create the domain (admin password required).
sudo container system dns create test
```
Make it the default DNS domain by setting `dns.domain` in `~/.config/container/config.toml`
(there is no `property set` command — properties are edited in the config file):
```toml
[dns]
domain = "test"
```
```bash
# Now a named container is reachable as <name>.<domain>.
container run -d --name my-web-server --rm web-test
curl http://my-web-server.test
# List / remove domains.
container system dns list
sudo container system dns delete test
# Confirm the effective domain.
container system property list # shows [dns] domain = "test"
```
Note: `container system property` only supports `list`/`ls` — set `dns.domain` via `config.toml`, not a CLI setter. A container named `my-web-server` with domain `test` answers at `my-web-server.test`.
---
### Custom isolated networks (macOS 26)
The entire `container network` group **requires macOS 26** (absent on macOS 15, which has only the single default subnet).
```bash
# Create an isolated network.
container network create foo
# With custom subnets.
container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
# Attach a container to it.
container run -d --name my-web-server --network foo --rm web-test
# List / delete (detach all containers first).
container network list
container stop my-web-server
container network delete foo
```
Note: networks are mutually isolated — a container on `foo` has no connectivity to containers on `default`.
---
### Inspect a container
```bash
# Full pretty JSON (one or more IDs).
container inspect my-web-server
# Extract specific fields with jq.
container inspect my-web-server | jq -r '.[0].status'
container inspect my-web-server | jq -r '.[0].networks[0].address'
# Table/JSON listing of all containers, including stopped.
container ls --all
container ls --all --format json | jq '.[] | [.configuration.id, .networks[0].address]'
```
Note: `inspect` requires at least one ID; a missing ID errors. `container image inspect <ref>` does the same for images.
---
### View logs
```bash
# Application stdio.
container logs my-web-server
# Follow live output.
container logs -f my-web-server
# Last N lines (-n is short-only; there is no --lines).
container logs -n 100 my-web-server
# VM boot / init logs.
container logs --boot my-web-server
```
Note: `--boot` shows the VM kernel + `vminitd` init log, useful for boot/networking diagnostics.
---
### Exec into a running container
```bash
# Interactive shell in a running container.
container exec -it my-web-server sh
# One-off command.
container exec my-web-server ls /content
# With env / as a specific user.
container exec -e DEBUG=1 -u root my-web-server env
```
Note: `exec` targets a **running** container and shares the same process options as `run` (`-e`, `--env-file`, `-u`, `-w`, etc.).
---
### Copy files in and out
Exactly one side must be a `container_id:path` reference. The container must be running.
```bash
# Host -> container.
container cp ./config.json my-web-server:/etc/app/
# Container -> host.
container cp my-web-server:/var/log/app.log ./logs/
```
Note: `container copy` and `container cp` are the same command.
---
### Resource limits (CPU / memory)
Container defaults: 4 CPUs, 1 GiB RAM. Builder VM defaults: 2 CPUs, 2 GiB RAM.
```bash
# Per-container limits on run/create.
container run --rm --cpus 8 --memory 32g docker.io/library/alpine:latest sh
# Short flags.
container run --rm -c 2 -m 1G docker.io/library/node:latest
# Size the builder VM before a heavy build.
container builder start --cpus 8 --memory 32g
container build -t big-app .
# Change a running builder: stop, delete, restart with new limits.
# ⚠️ `builder delete` discards the BuildKit builder and its layer cache — the next build
# re-fetches base images and rebuilds every layer from scratch.
container builder stop
container builder delete
container builder start --cpus 8 --memory 32g
```
Note: memory accepts `K`/`M`/`G`/`T`/`P` suffixes. Monitor live usage with `container stats` (`--no-stream` for a single snapshot).
---
### Cleanup: stop, remove, prune
> **⚠️ Scope matters.** The `-a`/`--all`/`prune` variants act on **every** container, image,
> volume, or network on the machine — not just this project's. `container volume prune` and
> `container image rm --all` permanently delete data belonging to *unrelated* containers. To clean
> up after one workload, use the **targeted** commands (by name/tag); reach for the machine-wide
> ones only when you deliberately want a full sweep. Do not paste this whole block blindly.
```bash
# --- Targeted (scoped to named resources — safe for cleaning up one workload) ---
container stop my-web-server # stop one container
container delete my-web-server # rm is an alias; add -f to remove a running one
container image delete web-test # remove one image by name/tag
container volume rm data # remove one volume by name
# --- Machine-wide (destroys ALL matching resources — use deliberately) ---
container stop -a # stop every running container
container rm --all # delete every container
container image rm --all # delete every image
container image prune # remove dangling (untagged) images only
container image prune -a # remove every image not used by a container
container volume prune # delete every volume with no container reference
container network prune # macOS 26+; delete every unused network
container prune # remove all stopped containers
# Check disk usage across images/containers/volumes.
container system df
```
Note: `container prune` only removes **stopped** containers. `image prune` (no `-a`) removes only dangling images.
---
### Upgrade / downgrade / uninstall
Both scripts install to `/usr/local/bin`. **Stop the services first.**
```bash
# Always stop before upgrading/downgrading.
container system stop
# Upgrade to the latest release.
/usr/local/bin/update-container.sh
# Downgrade: uninstall (keep user data with -k), then pin the previous release.
/usr/local/bin/uninstall-container.sh -k
/usr/local/bin/update-container.sh -v 0.7.1 # replace with the version you want
# Restart after any change.
container system start
# Uninstall. Default to -k (keep user data) unless you truly want everything gone.
/usr/local/bin/uninstall-container.sh -k # keep images/containers/volumes for a later reinstall
/usr/local/bin/uninstall-container.sh -d # ⚠️ also deletes ALL user data (images, containers, volumes) — irreversible
```
Note: you can also upgrade/downgrade by re-downloading and double-clicking the signed `.pkg`. `-v` pins `update-container.sh` to a specific version.
---
### Troubleshooting
```bash
# "XPC connection error" / commands hang -> services aren't running. Start them.
container system start
# Confirm services are up.
container system status
# View service logs (default last 5m; --last <n>[m|h|d], -f to follow).
container system logs
container system logs --last 1h
container system logs -f
# Add --debug to any command for verbose output (or set CONTAINER_DEBUG).
container --debug run --rm docker.io/library/alpine:latest true
# Version info for CLI + API server.
container system version
```
Note: an XPC/connection error means the background API server isn't started — run `container system start`. Feature gates to keep in mind: container-to-container access and the `container network` group require **macOS 26** (both unavailable on **macOS 15**). The CLI refuses to run under Rosetta — Apple silicon only.