Apple Container Skill
Operate Apple's container CLI as a native macOS Linux container runtime. This skill should guide the agent's choices, not just provide command syntax.
First Moves
Before doing real work, establish whether the host and runtime are usable:
sw_vers
uname -m
command -v container
container --version
container system status
container system version
- Require Apple silicon (
arm64) and macOS 26+ for supported workflows. Apple does not support older macOS releases; do not present macOS 15 workarounds as supported operation. - Record the CLI and service versions, compare them with the current signed release, and make sure they match before real work. As of September 2026, 1.4.1 is the security baseline for untrusted images, image archives, registries, or container identifiers; 1.3.1 and 1.4.1 fixed path traversal, host-file access, credential handling, and related OCI defects. Upgrade before inventing workarounds for defects already fixed upstream.
- If the CLI is missing, prefer Apple's signed installer from the GitHub releases page. Homebrew can work, but if
container system startfails with missing plugins after a Homebrew install, upgrade/reinstall the formula. - Start services with
container system startwhen status shows they are stopped. First start may prompt to install the recommended Linux kernel. - Treat installed
container <command> --helpas the authoritative command surface. Release summaries and even generated command references can lag the shipped parser; do not infer Docker subcommands or flags. - Run a smoke test before blaming application code:
container run --rm docker.io/library/alpine:latest sh -lc 'uname -a; nslookup github.com'
For an unknown or flaky environment, run the bundled diagnostic:
bash skills/apple-container-skill/scripts/diagnose.sh
Choose The Right Runtime Shape
- Use
container runfor disposable app containers, one-shot Linux commands, project dev shells, image smoke tests, and services whose state should live in bind mounts or named volumes. - Use
container machinefor a long-lived Linux workspace: repeated distro testing, system services, VS Code Remote SSH, a persistent root filesystem, or "edit on macOS, build inside Linux" loops. - Use experimental
container k8sonly for a disposable local single-node Kubernetes cluster. Do not present it as a production or multi-node Kubernetes runtime, and warn that it writes or updates kubeconfig entries. - Do not describe machines as merely "persistent containers." A machine is a convenience wrapper around a container, a separate persistent root disk, and host integration. It maps the host user, forwards SSH agent support, and mounts the macOS home at
/Users/<user>while the Linux user's$HOMEis/home/<user>. - Current Container Machine images need
/sbin/init. Apple Container 1.3 relaxed machine path restrictions and current Apple guidance usesalpine:latestdirectly; on current releases, try a requested image that contains/sbin/initbefore deriving a custom image. Build an OpenRC- or systemd-capable image when init is absent or the workflow needs managed system services. - For scripted machine commands, prefer an option terminator:
container machine run -n dev -- whoamiorcontainer machine run -n dev -- /bin/sh -c 'whoami; pwd; echo "$HOME"'. Avoid-iin heredoc/non-interactive scripts because it can consume the rest of the script from stdin.
Machine Image Selection
When the user names a distro image, preserve the distro choice but choose the runtime shape correctly:
- For one-shot commands or app containers, use the requested image directly with
container run. - For
container machine, check that the requested image contains/sbin/init. On Apple Container 1.3+, use a standard image directly when it satisfies that contract; Apple's current quickstart usesalpine:latest. - If
/sbin/initis absent, or the user needs systemd-managed services, explain the requirement and derive a machine image from the requested base instead of silently switching distros. - For Alpine machines, install both
openrcandopenrc-init, add related user/network tools, setCMD ["/sbin/openrc-init"], then build a local*-machineimage. Installing onlyopenrcand using BusyBox/sbin/initcan start and immediately shut down. - For Ubuntu/Debian machines, add systemd, dbus, sudo, SSH/network tools as needed, set the systemd target, and build a local
*-machineimage. - On Apple Container 1.2 or older, upgrade before turning missing OpenRC, masked-path, or read-only-path failures into a permanent custom-image workaround.
Safety Rules
Ask before:
- Running
sudo, installing, upgrading, uninstalling, or changing DNS resolver entries. - Global cleanup:
container prune,container image prune,container volume prune,container network prune, or deleting all resources. - Deleting named machines, volumes, images, or containers that were not created for the current task.
- Editing
~/.ssh/config, changing host firewall/VPN settings, or widening bind mounts beyond the project directory. - Passing kernel arguments that disable or weaken security controls. Use
--kernel-argonly for a documented kernel-level requirement, not ordinary application configuration. - Passing
NONEto--masked-pathor--read-only-path, because it clears runtime security defaults rather than adding a restriction.
Prefer graceful stops (container stop, container machine stop) before forceful deletion or container kill.
Practical Defaults
Common project shell:
container run --rm -it -v "$PWD:/work" -w /work docker.io/library/ubuntu:24.04 bash
Build and run an image:
container build -t local/app:dev .
container run --rm -p 8080:8080 local/app:dev
Long-lived machine:
container machine create docker.io/library/alpine:latest --name dev --set-default --cpus 4 --memory 8G
# Run the first command from a real terminal so initial user setup has a host PTY.
container machine run -n dev -- /bin/sh -c 'whoami; pwd; echo "$HOME"'
container machine stop dev
Important Current Behaviors
container system property get,set, andclearwere removed in 1.0. Use~/.config/container/config.tomlfor defaults andcontainer system property listonly to inspect effective config.- Apple's signed installer places the CLI at
/usr/local/bin/container; check that path directly when a fresh shell cannot findcontainer. - Apple Container 1.3 removed the registry
--scheme autovalue. Current releases accept onlyhttps(the default) or an explicithttp; use HTTP only for a deliberately trusted local registry. - Apple Container 1.3.1 and 1.4.1 include security fixes for crafted identifiers, image layers/layouts, registry authentication, and host-file access. Do not use an older runtime for untrusted OCI inputs merely because its basic smoke test passes.
container buildmay leave the BuildKit builder running. If a validation task must leave no runtime processes, inspectcontainer builder status, then usecontainer builder stopandcontainer builder delete.- Apple Container 1.2.1 added
container build --ssh default; use it for private build dependencies instead of copying SSH keys into the build context or image. - Host-to-container traffic should usually use
-p/--publish; if it fails, check that the app listens on0.0.0.0inside the container. - For Unix sockets, choose by direction: bind-mount (
-v) a socket that already exists on the host into the container; use--publish-socket host_path:container_pathwhen the process in the container creates the socket and the host must reach it. For non-root clients, verify ownership and mode on both endpoints. Apple Container 1.1 fixed non-root socket-mount access. - Apple Container 1.1 also fixed relative local paths for
container cp; on older versions, use absolute paths or upgrade rather than debugging a correct relative path. - Apple Container 1.2 prevents an untrusted image's bare
ENVentry from implicitly copying a same-named host variable. Still make inheritance explicit: use-e NAMEonly when host passthrough is intended, and preferKEY=value, an env file, or a secret mechanism for reproducible runs. - Use repeatable
--kernel-arg key=valueonly for a kernel/security/debug requirement and verify the effective command line withcat /proc/cmdline. Do not use it for application settings or casually replace security defaults such aslsm=landlock. - The 1.4.1 release does not expose
container run --stop-signal. PutSTOPSIGNALin the image for a reusable default or usecontainer stop -s SIGNALfor an operator-selected signal. --masked-pathand--read-only-pathare experimental additive isolation controls onrunandcreate. Verify them with the installed help, prefer adding paths, and never clear the runtime defaults withNONEwithout explicit approval.container clean <running-container...>reclaims unused filesystem space from each container root filesystem and its named volume mounts. It is scoped, requires running containers, and is not a substitute for global prune commands.container system statusgained additional host, client, path, and resource fields in 1.4.1. Consume structured output by field name and tolerate additive fields rather than depending on the old shape.- Treat named volumes as single-attachment unless the workflow has proven otherwise; do not assume the same named volume can be attached concurrently to multiple running containers.
- Container-to-host traffic does not use Docker's magic host alias. Configure a localhost DNS domain:
sudo container system dns create host.container.internal --localhost 203.0.113.113
This can disable Private Relay, and packet-filter rules may need recreation after reboot.
container networkuser-defined networks require macOS 26+. Container name resolution currently works only on the default network; use inspected IPs on custom networks rather than promising Docker-style service discovery.- There is no
container compose. Translate a simple trusted stack into explicit build/run/readiness/teardown commands, or keep a Compose-capable runtime when Compose semantics are required. - If networking worked and then fails after VPN or endpoint security changes, suspect vmnet/VPN routing before changing application code.
- A machine's first command may need a real host terminal while user setup completes. If a headless first run fails with
Operation not supported by device, retry the initialization from a PTY; subsequent non-interactivemachine run ... -- commandcalls can be scripted. Do not add-ito a heredoc or unattended job.
References
- Read references/workflows.md for common development, build, network, registry, volume, machine, filesystem-reclamation, and experimental Kubernetes playbooks.
- Read references/troubleshooting.md when service startup, DNS, VPN, vmnet, builder, Rosetta, port publishing, or machine mode fails.
- Read references/commands.md for concise command coverage after you know which workflow you need.
Scan to join WeChat group