# container runs each Linux container in a lightweight VM, and needs macOS 26

> Apple's Swift tool for creating and running OCI-compatible Linux containers on Apple silicon. Installation is a signed package that writes under /usr/local, the system service is started separately, and release numbers are product versions rather than semantic versions.

**apple/container** — A tool for creating and running Linux containers using lightweight virtual machines on a Mac. It is written in Swift, and optimized for Apple silicon.

- Repository: https://github.com/apple/container
- Website: https://apple.github.io/container/documentation/
- Stars: 50,387 · Forks: 1,806
- Language: Swift
- License: Apache-2.0
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/apple-container

## Containers are lightweight virtual machines, and images stay OCI-compatible

The runtime model is the whole idea. container creates and runs Linux containers as lightweight virtual machines on the Mac rather than sharing a host kernel, and it is written in Swift and optimized for Apple silicon. Low-level container, image and process management is delegated to the separate Containerization Swift package, which the README names as the dependency.

The image side is deliberately unremarkable, and that is the point. container consumes and produces OCI-compatible container images, so you can pull and run images from any standard container registry, push images you build to those registries, and run the same images in any other OCI-compatible application. Nothing about the image format is Apple's, which means the artifact you build here is not trapped in this tool.

The consequence for a reader is that the two halves of the project make different promises. The image half is portable and boring, and the runtime half is the specialised part. Judging this project means judging a macOS-native runtime, not judging a container image format.

## The install writes under /usr/local and the service starts as a separate step

There is no package manager line to copy. Installation starts by downloading the latest signed installer package from the GitHub release page, double-clicking it, and entering an administrator password so the installer can place files under /usr/local. After that the system service has to be started on its own:

```bash
container system start
```

A first container follows, and it pulls the image as part of running it:

```bash
container run --rm alpine echo hello
```

That command pulls the alpine image, runs it in a lightweight Linux VM, prints hello, and removes the container when it exits because of the --rm flag. The administrative password in the install step is the part worth planning for: on a managed Mac the install needs someone who can authorize a write into /usr/local, so an unattended setup has to be scripted around that prompt. Documentation for the fuller path sits in a tutorial under docs/tutorials and a how-to under docs.

## Apple silicon and macOS 26 are both hard floors

The requirements section is short and the limits are absolute. You need a Mac with Apple silicon to run container, and container is supported on macOS 26, because it takes advantage of new features and enhancements to virtualization and networking in that release. Older versions of macOS are not supported, and the maintainers state that they typically will not address issues that cannot be reproduced on macOS 26.

That last sentence is the one to weigh. A bug report from a macOS 15 machine is not merely unsupported, it is a report the maintainers have said they will not chase. A team with a mixed macOS fleet cannot standardize on this tool until every developer machine is on 26, and a team that upgrades slowly inherits an ungated bug queue rather than a supported configuration.

For anyone outside that envelope the answer is simply no: no Intel Mac, no Linux host, no older macOS. The tool is not a cross-platform runtime with a Mac preference, it is a Mac-only runtime, and the source distribution keeps that shape, with a Swift package manifest and a single build path documented in BUILDING.md.

## Release numbers are product versions, so the semver habits do not apply

The project status section says the release versions are product versions, not semantic versions. There is no compatibility promise attached to a minor bump the way there would be with semver, and the compatibility rules are spelled out separately and unevenly.

The CLI generally preserves backward compatibility within a major release, with the acknowledgement that breaking compatibility may be necessary in the odd case. Features marked experimental, and the k8s subcommand is given as the example, may change and do not guarantee backward compatibility at all. The container-apiserver XPC API preserves both forward and backward compatibility within a major version, which is the strongest guarantee in the document. Other non-public XPC helpers guarantee nothing, and the application data provides forward compatibility only, guaranteed within one major version, so upgrading to a newer major version may require a specific upgrade path.

Read that as a checklist before you automate anything against this CLI. A script calling the k8s subcommand has no stability to lean on, and code touching the non-public XPC helpers is the most exposed part of the surface.

## Downgrading means uninstalling with -k, then naming the version explicitly

Upgrades and downgrades both require the existing container to be stopped first, and forgetting that is the first way to end up with two states on one machine:

```bash
container system stop
```

Upgrading to the newest release is a single script, installed to /usr/local/bin:

```bash
/usr/local/bin/update-container.sh
```

Downgrading is a two-step sequence with explicit version numbers. The existing container is uninstalled while keeping user data, and then the update script is told which version to install:

```bash
/usr/local/bin/uninstall-container.sh -k
/usr/local/bin/update-container.sh -v 0.3.0
```

The two flags on the uninstall script are the other half of the mechanism. The -k flag keeps your user data so it survives a reinstall, and -d removes it. Full uninstall therefore has three outcomes across two scripts: upgrade in place, downgrade while retaining data, or remove the tool and the data with -d. Whichever you pick, the service is left stopped and needs container system start again.

## The Makefile builds an unsigned package into a staging directory

The build system shows what a signed-installer project has to arrange. BUILD_CONFIGURATION defaults to debug and WARNINGS_AS_ERRORS defaults to true, and a shared swift build invocation is assembled at /usr/bin/swift with the warnings flag and testing enabled. Release stamping comes from git describe --tags --always for the version and git rev-parse HEAD for the commit, so a build without git metadata produces a binary with no version in it.

Paths are laid out for packaging rather than for running. DEST_DIR defaults to /usr/local/, the staging directory is bin/$(BUILD_CONFIGURATION)/staging/, and the artifact the build is aiming at is bin/$(BUILD_CONFIGURATION)/container-installer-unsigned.pkg, with a parallel bundle path for the container dSYM and its zip. The word unsigned is the point: the repository builds an unsigned package, and signing is a separate concern with its own directory in the tree.

The rest of the layout is a normal Swift package with unusual neighbours. Sources/ and Tests/ hold the code, Package.swift and Package.resolved the dependency graph, a separate Protobuf.Makefile suggests generated interfaces, and .swift-format with a nolint file is committed, so formatting is enforced rather than left to taste. There are also skills/, signing/, assets/, examples/ including a container-machine-vscode directory, and a licenserc.toml next to the Apache-2.0 LICENSE and NOTICE.md.

## Interop with other OCI tools is the stated path, not a comparison

What this project does not do is worth being plain about. It does not run on Intel Macs, on Linux, or on anything below macOS 26, and the maintainers tie their support commitment to that release. It does not define its own image format, which is a feature for portability and a limit on differentiation. It does not promise semver behaviour across releases, and the experimental markers are real carve-outs rather than a courtesy.

Where the README does describe an outside boundary, it is the image boundary. You can push what you build to any standard container registry and run it in any other OCI-compatible application, which makes the honest comparison a question of runtime rather than of image: if your images already follow the OCI image spec, the choice is which runtime executes them, and this one is the option that runs each container as a lightweight VM on Apple silicon under macOS 26.

The project names no competing tool and makes no performance or footprint claim, so nothing here settles which runtime is faster or lighter. It settles what this one is: a Mac-native Swift runtime, built on a separate package, released as product versions, with an installer that needs administrator rights.

## Conclusion

Adopt container if your workload is Linux containers on an Apple silicon Mac and you want the images to stay OCI-compatible so the same artifact runs elsewhere. Do not adopt it for a mixed fleet: it needs Apple silicon and macOS 26, older macOS releases are explicitly out of scope, and the maintainers say they will not address issues they cannot reproduce on macOS 26. Two things to check before you plan around it. Read what the version numbers mean, because releases are product versions rather than semver, an experimental subcommand such as k8s carries no compatibility promise, and application data is only guaranteed forward compatible within one major version. And decide the downgrade path early, because moving down a version means running the uninstall script with -k first and then update-container.sh with an explicit -v, and forgetting the stop step leaves the old service running.

## FAQ

### What are the system requirements for container?

A Mac with Apple silicon running macOS 26. Older macOS versions are not supported, and the maintainers say they typically will not address issues that cannot be reproduced on macOS 26.

### How do I install container on a Mac?

Download the latest signed installer package from the GitHub release page, double-click it, and enter your administrator password so it can place files under /usr/local. Then start the service with container system start.

### Can container run images from any container registry?

Yes. container consumes and produces OCI-compatible container images, so you can pull and run images from any standard container registry, and push images you build to those registries to run in other OCI-compatible applications.

### How do I downgrade container to an older version?

Stop the existing container with container system stop, run /usr/local/bin/uninstall-container.sh -k to uninstall while keeping your user data, then run /usr/local/bin/update-container.sh -v with the version you want. Start the service again afterwards.

### Are container release versions semantic versions?

No. The project states that its release versions are product versions, not semantic versions, and that features marked experimental, such as the k8s subcommand, may change without a backward compatibility guarantee.

## Sources

- [Official documentation](https://apple.github.io/container/documentation/)
- [Official README](https://github.com/apple/container#readme)
- [Project repository](https://github.com/apple/container)
- [Release notes](https://github.com/apple/container/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/apple-container
