A cloud architect’s field notes on apple/container v1.2.0, for the devops engineers and application specialists who have to make the “what runs on our Macs” decision.

A year ago I published Running Containers the Apple Way: A First Look into Apple Container on macOS. At the time, container was a WWDC 2025 curiosity: a Swift CLI with no stability guarantees and a limited list of things it could actually do. I was cautiously optimistic and noted, in effect, “watch this space.”

I watched the space. It moved.

container turned one year old on June 9, 2026, shipping its first stable 1.0.0 release, with CLI and XPC APIs frozen and patch-level compatibility guaranteed. Since then, it has continued to progress steadily: from 1.1.0 to 1.2.0 as of this writing, with the 1.2.0 release on July 29, 2026. This blog provides a validated, practical overview of the project’s current state: what’s changed, what can be built with it now, and how an architect can justify tooling choices to a platform team, highlighting where it truly integrates into a DevOps workflow and where it still falls short.

The short version, for the skim readers

container is a native macOS CLI written in Swift, designed specifically for Apple silicon. Unlike Docker Desktop, Colima, or OrbStack, which run containers inside a shared Linux VM, container operates in a lightweight, dedicated VM for each container. It handles OCI-compliant images, meaning you can run any image you pull with Docker and deploy any container built with it anywhere OCI images are supported.

A year in, the headline changes are:

  • 1.0.0 stability: The CLI and XPC API are now fixed at the minor-version boundary, allowing you to build tools on top without concern for breaking changes with each release.
  • Container machine: A new feature offering persistent, systemd-capable Linux environments with your Mac’s home directory and user account directly mapped.
  • Includes a TOML config file replacing the old UserDefaults settings, a proper container copy command, enhanced JSON/YAML output, container stats, capability management, custom init images, nested virtualization, and IPv6 networking.
  • Designed for Apple Silicon only, best used with macOS 26 (macOS 15 has limitations), and still lacking a built-in Compose feature after a year. That gap remains, and I plan to address it.

Details on requirements and how to install or upgrade.

container requires a Mac with Apple silicon. It is supported on macOS 26, where the Virtualization and networking improvements it depends on are available; macOS 15 is usable but has documented network limitations (containers can’t reach each other, and multiple isolated networks aren’t available).

If you’re new to it, grab the signed installer from the releases page and start the system service:

container system start

If you’ve already installed it from last year’s earlier versions, the upgrade can be done with a single script call; no need to uninstall beforehand.

container system stop
/usr/local/bin/update-container.sh
container system start

Downgrading remains straightforward, as Apple maintains an open escape route:

container system stop
/usr/local/bin/uninstall-container.sh -k     # -k keeps your data
/usr/local/bin/update-container.sh -v 0.3.0
container system start

Check what you’re running with:

container system version --format json

Under the hood: still one VM per container, and that’s the whole point

Apple’s architectural gamble from a year ago remains the same but has matured. Instead of booting a single large Linux VM to run all containers, as most Docker-on-Mac solutions do, it leverages the open-source Containerization Swift package to launch a minimal, dedicated lightweight VM for each individual container.

Practically, that buys you three things Apple is explicit about in its own technical overview:

  • Security: each container operates with complete VM isolation, using only essential utilities and libraries and without sharing a kernel namespace.
  • Privacy: when you attach host data to a container, you only mount what it requires. There’s no shared VM that requires pre-mounting everything “just in case.”
  • Performance: even with full VMs, boot times match those of containers in shared VMs, and the memory usage remains close to what the containerized process requires.

The CLI communicates with a launchd agent named container-apiserver, which oversees three groups of XPC helpers: container-core-images for handling images and content storage, container-network-vmnet for virtual networking, and a separate container-runtime-linux instance for each running container. This entire setup utilizes macOS’s Virtualization framework, vmnet, XPC, launchd, Keychain, and unified logging, giving it a seamless integration without any feelings of being added as an afterthought.

A note for capacity planning: the macOS Virtualization framework currently offers only limited support for memory ballooning. For example, if you launch a container with memory 16g, Activity Monitor might display significantly less memory usage because pages freed by the Linux guest aren’t always returned to the host. On a Mac running numerous memory-intensive containers, you might need to restart them periodically. This is a known and documented limitation, not a bug that requires extensive troubleshooting.

An example: build, run, and publish a web server

Start the system service and configure a local DNS domain (optional but recommended), which assigns a .test hostname to each named container.

container system start
sudo container system dns create test

Write a Dockerfile:

FROM docker.io/python:alpine
WORKDIR /content
RUN apk add curl
RUN echo '<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello, world!</h1></body></html>' > index.html
CMD ["python3", "-m", "http.server", "80", "--bind", "0.0.0.0"]

Build it:

container build --tag web-test --file Dockerfile .

Run it, detached, self-cleaning on exit:

container run --name my-web-server --detach --rm web-test

container ls confirms it’s up and shows its IP on the isolated vmnet subnet:

ID IMAGE OS ARCH STATE IP CPUS MEMORY STARTED
my-web-server web-test:latest linux arm64 running 192.168.64.3/24 4 1024 MB 2026–08–06T14:42:07Z
buildkit ghcr.io/apple/container-builder-shim/builder:0.13.0 linux arm64 running 192.168.64.2/24 2 2048 MB 2026–08–06T13:33:10Z

Hit it directly by IP, or by the .test hostname you set up earlier:

curl http://my-web-server.test

Watch it live with the resource-monitoring command that shipped this year:

container stats --no-stream my-web-server

Container ID Cpu % Memory Usage Net Rx/Tx Block I/O Pids
my-web-server 0.07% 37.30 MiB / 1.00 GiB 3.68 KiB / 0.59 KiB 19.86 MiB / 2.30 MiB 1

And publish it to any OCI-compliant registry; Docker Hub is the default, but you can point [registry].domain in ~/.config/container/config.toml anywhere:

container registry login some-registry.example.com
container image tag web-test some-registry.example.com/fido/web-test:latest
container image push some-registry.example.com/fido/web-test:latest

None of this needed Docker Desktop, a paid license, or an unwanted background VM.

What’s genuinely new since last year’s “first look”

  • container machine — the headline 1.0 feature

This feature distinguishes the container’s purpose. While ‘container run’ models a single application process, ‘container machine’ provides a persistent Linux environment booted from a standard OCI image, running its real init system, with your macOS username and home directory automatically mapped.

container machine create ubuntu:24.04 --name dev
container machine run -n dev whoami   # your host username, not root
container machine run -n dev pwd      # /home/<you> — your Mac home dir, mounted in
container machine run -n dev          # interactive shell

Set a default so you can drop -n, resize resources on the fly, and run real background services with systemd:

container machine set-default dev
container machine set -n dev cpus=4 memory=8G
container machine stop dev && container machine run -n dev -- nproc
container machine run -n dev -- systemctl start postgresql

For a DevOps audience, this straightforward solution addresses the need for a genuine Ubuntu environment to test deployment scripts while still accessing the same repository used in VS Code on a Mac. You edit directly on macOS, compile, run, and test on Linux without copying, rsyncing, or complex bind-mount configurations. Additionally, you can quickly create separate machines for each target distribution, such as Alpine, Ubuntu, or Debian, each sharing the same $HOME and dotfiles.

  • A TOML configuration file

The previous get/set subcommands for the UserDefaults-backed container system property have been removed, marking a documented breaking change in CLI version 1.0. Configuration is now stored in ~/.config/container/config.toml, offering greater transparency and simplifying templating across multiple engineering laptops.

container system property ls
[build]
cpus = 2
memory = "2048mb"
rosetta = true

[container]
cpus = 4
memory = "1gb"

[registry]
domain = "docker.io"

If you’re deploying this to a team, such a file is exactly the type you’d add to a dotfiles repository or distribute through MDM.

  • container cp, richer inspection, and container stats

container cp (host ↔ container file transfer) shipped in 1.0 after sitting open as a feature request since the very first weeks of the project. JSON, YAML, and TOML output for list/inspect across containers, images, networks, and volumes was normalized in the same release, which matters the moment you start scripting against this tool rather than typing commands by hand:

container ls --format json --all | jq '.[] | select(.status == "running") | [.configuration.id, .networks[0].address]'
  • Multiplatform builds and Rosetta-backed amd64

You can build for both architectures simultaneously and run the x86–64 version seamlessly using Rosetta translation:

container build --arch arm64 --arch amd64 --tag registry.example.com/fido/web-test:latest --file Dockerfile .
container run --arch amd64 --rm registry.example.com/fido/web-test:latest uname -a
  • Fine-grained Linux capabilities, custom init, and nested virtualization

Containers initially have a limited, documented ability set by default. You can explicitly add, remove, or reset these capabilities, which is especially helpful during compliance reviews to clearly define what a container is permitted to do.

container run --cap-drop ALL --cap-add SETUID --cap-add SETGID alpine id

— init provides a lightweight PID-1 that handles zombie processes and forwards signals for applications not originally designed to be PID 1. — init-image extends this functionality by allowing you to wrap vminitd with custom boot-time logic, such as an eBPF filter, a logging sidecar, or custom instrumentation, before the container’s main entrypoint executes. On M3-and-later hardware, virtualization offers nested virtualization within the guest, enabling teams to run hypervisor workloads inside their VM-based containers.

Isolated networks and IPv6

macOS 26 introduces the container network create feature, enabling the creation of multiple isolated vmnet subnets. This is useful for replicating a segmented network topology locally instead of relying on a single flat subnet:

container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
container run -d --name my-web-server --network foo --rm web-test

If your understanding of container is limited to what the WWDC 2025 build could do, here’s a comparison in one table.

Use cases: where this fits in a real DevOps workflow

Local development on a compliance-conscious Mac fleet. Docker Desktop’s licensing terms require a paid subscription for larger companies. The container is Apache-2.0 licensed, built by the OS vendor, and has no license restrictions, making it an easy choice for platform teams to standardize tooling across engineering.

Reproducibility for CI related tasks on Apple silicon runners. As more CI fleets adopt Apple silicon for cost savings and performance benefits, a native, scriptable, OCI-compliant runtime that operates without a licensed daemon simplifies the process. The JSON-first output introduced in version 1.0 significantly eases integration with pipeline tools.

Multi-distro compatibility testing simplifies the process by transforming the need to manually set up three VMs to check whether the install script works on Debian, Ubuntu, and Alpine into just three straightforward container creation commands. Each command already includes your dotfiles and repository, making setup faster and more efficient.

Security-sensitive workloads require true isolation rather than just namespace separation. Using per-container VMs with a minimal attack surface presents a significantly different security risk compared to shared-kernel containers. This distinction is especially important for anyone running third-party or less-trusted images locally.

Cross-architecture builds without needing a second machine — combining arm64 and amd64 architectures in one build command. Powered by Rosetta-backed emulation, which is significantly faster than QEMU translation, this setup simplifies testing for compatibility with our x86 datacenter without the need to provision an Intel machine.

Prototyping systemd-dependent services. If your production environment uses real systemd units, container machine allows you to develop in an environment that closely matches it, rather than simulating it with a foreground process inside a standard container.

Onboarding and standardized development environments can be effectively managed with a Dockerfile documented in container-machine.md and a one-line command to create a container machine. This approach serves as a practical alternative to a golden AMI or a Vagrantfile when onboarding new engineers on a Mac. It provides a reliable Linux environment within minutes, complete with preconfigured dotfiles, at minimal cost other than disk space.

Auditable and scriptable tools designed for platform teams. The transition to standardized JSON, YAML, and TOML output covering list and inspect, along with a templateable and diffable config file, provides exactly what a platform engineering team requires. It enables the development of internal tools on a container runtime without the need to reverse-engineer custom text outputs.

Reasons to choose it and areas where I’d exercise caution

Choose this option if you’re using Apple Silicon, need a native, license-free OCI runtime with genuine VM-level isolation, appreciate a stable CLI and API that Apple now guarantees, and don’t require multi-container orchestration by default.

Hold off on using it or combine it with another tool if your workflow relies heavily on Docker Compose. Even after a year, there’s still no official container-compose command. Apple has recognized this gap, and the community has filled it with tools like container-compose, but these are not as polished as the native experience users expect from Compose. If your team mainly works with Compose files, plan to spend time assessing these community solutions before making a decision. You’re also limited to Apple silicon devices and, for full features, macOS 26 or later—there’s intentionally no cross-platform support. Additionally, the memory ballooning issue mentioned earlier should be noted in your onboarding documentation so that team members don’t report it as a bug, since it’s an expected behavior.

Where it sits next to Docker Desktop and OrbStack

I won’t claim to have conducted a comprehensive head-to-head comparison in this article, so I will concentrate on presenting only verified facts rather than referencing others’ data. Both Docker Desktop and OrbStack operate containers within a shared Linux VM, whereas containers generally run in isolated environments. This fundamental structural difference largely accounts for other variations: containers provide enhanced per-workload isolation and reduce the potential impact of issues, but they also come with a less developed ecosystem, lacking features like Compose, having fewer plugins, and supporting fewer third-party integrations. If your main concerns are “native, free, isolated, and you’re open to early adoption,” then container is a practical choice now, compared to a year ago. However, if you’re seeking a seamless, “drop-in” Compose replacement with no changes to your workflow, that solution isn’t quite ready yet.

The verdict, one year in

Last year’s piece ended with “watch this space.” After a year, a major stable release, and a minor 0.2 update, the honest update is: it was worth watching. The container evolved from a WWDC demo with no stability guarantees to version 1.2.0, featuring a frozen CLI and API, a new persistent environment model with a container machine, and numerous operational improvements, including TOML config, cp, stats, capability control, and multiplatform builds. I would now consider it for teams evaluating container tools on Apple silicon. Although it’s not yet a full replacement for Docker Desktop, mainly because of the Compose gap for DevOps engineers and application specialists working natively on Apple silicon, it’s no longer just a curiosity. It has become part of the infrastructure.