Contained: a native macOS GUI for Apple's container CLI
A native macOS app for Apple's Container CLI
At a glance
- What is it?
- Contained puts a SwiftUI control surface over Apple's container command line, previewing the exact command before privileged operations. It is source-available under PolyForm Noncommercial, requires macOS 26 on Apple silicon, and ships nightly builds.
- Who is it for?
- Adopt Contained if you already run Apple's container CLI on an Apple silicon Mac with macOS 26 and want a visual layer that still shows the underlying command before privileged operations. Do not adopt it if you need a commercial licence, a stable release, or Intel Mac support, since the README states macOS 26 or later on Apple silicon and the current line is pre-1.0 nightly.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What Contained solves for Apple container CLI users
Apple's container CLI is a terminal tool. Contained is a native macOS app that gives containers, images, volumes, networks, registries, logs, templates, health and restart behaviour a SwiftUI interface while keeping the underlying command line visible. The README describes it as a control surface, not a replacement runtime: the app talks to Core.Orchestrator, and Core owns the adapter-specific argv and process details.
The audience is narrow and specific. You need macOS 26 or later on Apple silicon, and Apple's container CLI 1.0.0 on PATH. If you are on an Intel Mac or an older macOS release, the app is not for you. If you already work comfortably in the terminal, the value is mostly in the run and edit form, the command preview before privileged operations, and the Compose import that turns a file into an editable form instead of an opaque stack.
One design decision stands out: personalization and app-managed metadata stay local to Contained. Tints, icons, nicknames and graph settings live in the app, so the container resources themselves remain clean when you use them directly from the terminal. That is a deliberate boundary, and it means the app does not become the source of truth for your resources.
How the SwiftPM packages and CLI adapter fit together
The repository is a root SwiftPM package with a small launcher plus the shared app implementation, consuming three standalone local packages. ContainedCore handles backend orchestration through Core.*: runtime descriptors, canonical container models, command previews, Compose import and export, the Apple container adapter internals, metrics, typed errors and migration planning. ContainedUI owns the visual system through UI.*: tokens, materials, cards, panels, controls, state views and charts. ContainedUX covers interaction infrastructure through UX.*: safe areas, morphing, source measurement and panel placement.
The ContainedApp target is the SwiftUI shell: navigation, feature views, stores, history, settings, Sparkle, presentation mapping, localization and app policy. A separate Contained target is the SwiftPM executable launcher used by command-line builds and bundle scripts.
Integration is CLI-based rather than private-framework based. That matters for upgrade cost: when Apple changes the container CLI surface, the adapter internals in ContainedCore are where the adjustment happens, not scattered across the UI. A separate ContainedCoreFixtures product exists for deterministic test and preview data under Core.Fixtures.*, and the README states normal app and distributable bundle targets do not link it.
Installing Contained and running your first container
The README points to the Releases page for the latest .dmg. There is no Homebrew formula or package manager command documented, so installation is a download and drag. Sparkle updates are built in, and during pre-1.0 development fresh installs default to the Nightly channel so they receive current builds. The channel can be changed in Settings, under Updates, between Stable, Beta and Nightly.
Before launching, confirm the requirement the README lists: Apple's container CLI 1.0.0 must be on PATH. The app is a control surface, so it has nothing to drive without that binary. The README does not give a version-check command for it, so check that the CLI is installed and reachable before opening the app.
For local development the README gives two entry points over the same package graph. Xcode is for native build and run, SwiftUI previews and manual UI iteration. Package.swift is for CI, tests, bundle generation, signing, notarization, release notes and appcasts.
open Contained.xcworkspace
swift build
swift testThe README also lists xcodebuild invocations against the Contained scheme in Debug configuration for build and test, and a packaging script for producing the app bundle.
xcodebuild -workspace Contained.xcworkspace -scheme Contained -configuration Debug build
xcodebuild -workspace Contained.xcworkspace -scheme Contained -configuration Debug test
./Scripts/package.sh app debug
open Contained.appThe first two lines build and test through the workspace scheme, the third produces a debug bundle through the script, and the fourth opens it. Maintainers use Scripts/package.sh notarized and Scripts/appcast.sh generate for signing, notarization, DMG creation, GitHub release notes and Sparkle appcasts, which tells you the release path is scripted rather than manual.
Where Contained is the wrong tool
The licence is the first hard boundary. Contained is source-available and free for non-commercial use under the PolyForm Noncommercial License 1.0.0, and the Contained name and branding are reserved per the NOTICE file. If your use is commercial, this licence is not written for you, and the repository does not offer a commercial option in the README. That is a licensing question for your own counsel, not something the README resolves.
The second boundary is the platform. macOS 26 or later on Apple silicon is not a soft recommendation. There is no documented Intel build, no Linux build and no Windows build. Anyone looking for a cross-platform container GUI is in the wrong repository.
The third is maturity. The README labels the project pre-1.0, and the release list shows nightly builds such as v1.0.0-nightly.224+0accbf6 dated 2026-09-08. The README also describes several capabilities as experimental, including toolbar panels, morph surfaces, the command palette, Docker Hub search, the image build workspace, keyboard shortcuts and Compose import, all reachable from Settings. If you need a frozen, supported release with a deprecation policy, this is not it yet.
One more limitation worth naming: the README does not document rollback. There is no described way to revert an app-managed change to a container, which is exactly the kind of gap that matters when the app edits a running resource.
Contained compared with Docker Desktop and the raw CLI
Docker Desktop is the obvious alternative, and the difference in approach is architectural rather than cosmetic. Docker Desktop ships its own engine and its own daemon, and the GUI is one surface over that stack. Contained does not ship an engine. It drives Apple's container CLI, and the README is explicit that integration is CLI-based rather than private-framework based. If you already committed to Apple's runtime on Apple silicon, Contained adds a UI without adding a second runtime.
That choice cuts both ways. You inherit whatever the container CLI supports, and you inherit its 1.0.0 requirement. A feature that exists in Docker's engine but not in Apple's CLI is not something Contained can conjure, because Core is generating argv for the Apple adapter.
The other alternative is the terminal itself. Contained does not hide it. The README lists previewing the exact container command before privileged run and edit operations as a feature, and Compose files import into editable run forms rather than launching opaque stacks. If you want a GUI that abstracts the command away entirely, this is the opposite of what you want. If you want a GUI that shows its work, the command preview is the point.
For teams, the local-only metadata design is the real differentiator against a GUI that stores state in the engine. Contained keeps personalization and app-managed metadata inside the app, so a colleague running the same container from a terminal sees clean resources.
Maintenance, nightly cadence and licence implications
The last push was on 2026-09-08, and the most recent release is v1.0.0-nightly.224+0accbf6 from the same day. The two prior releases, v1.0.0-nightly.220+c461951 and v1.0.0-nightly.217+d4a2fea, are dated 2026-08-15 and 2026-08-14. That pattern suggests a nightly cadence rather than a stable release train, and the README confirms fresh installs default to Nightly during pre-1.0 development.
Upgrade cost is partly handled by Sparkle, which is built in and lets you switch between Stable, Beta and Nightly in Settings. The cost that Sparkle does not cover is churn: nightly builds change behaviour, and the README flags a list of features as experimental. A team pinning to a nightly build should expect to re-verify the run and edit form and Compose import after updates, because those are the surfaces where a changed argv or model would show up first.
On licensing, the PolyForm Noncommercial License 1.0.0 is not an OSI open source licence. It permits non-commercial use and reserves the Contained name and branding. The repository carries a NOASSERTION licence identifier, which reflects that the LICENSE and NOTICE files define terms rather than a standard SPDX identifier. Whether your specific use counts as non-commercial is a legal question, and this article cannot answer it. The practical step is to read LICENSE and NOTICE before you build it into anything that generates revenue.
Editorial conclusion
Adopt Contained if you already run Apple's container CLI on an Apple silicon Mac with macOS 26 and want a visual layer that still shows the underlying command before privileged operations. Do not adopt it if you need a commercial licence, a stable release, or Intel Mac support, since the README states macOS 26 or later on Apple silicon and the current line is pre-1.0 nightly. Verify first that Apple's container CLI 1.0.0 is on your PATH, that the PolyForm Noncommercial terms fit your use, and that your Compose files survive the import into editable run forms rather than launching as opaque stacks.
Frequently asked questions
What is Contained and what is it used for?
Contained is a native macOS control surface for Apple's container CLI. It gives containers, images, volumes, networks, registries, logs, templates, health and restart behaviour a SwiftUI interface while keeping the underlying command line visible.
What does Contained require to run?
The README lists macOS 26 or later on Apple silicon, Apple's container CLI 1.0.0 on PATH, and Xcode 26 with Swift 6.2 or later for local development.
How do I install Contained on macOS?
Download the latest .dmg from the Releases page. Sparkle updates are built in, and fresh installs default to the Nightly channel during pre-1.0 development, which can be changed in Settings under Updates.
Is Contained free to use?
Contained is source-available and free for non-commercial use under the PolyForm Noncommercial License 1.0.0. The Contained name and branding are reserved, as stated in the NOTICE file.
Community notes