A native macOS menu-bar app and dashboard for Apple's container CLI. Inspect, control, and shell into containers without leaving the menu bar.
The app shells out to the container binary — there is no daemon or framework binding. It expects the binary at /usr/local/bin/container (the official package location), /opt/homebrew/bin/container, or a custom path configured in Settings.
Add the Open Southeners Homebrew tap, then install ContainerApp:
brew tap open-southeners/tap
brew install --cask open-southeners/tap/container-app- System status indicator (running / stopped / not installed)
- Up to five running containers with quick Logs, Shell, and Stop actions
- Start / Stop System controls
- Show All Containers opens the full dashboard
- Refreshes automatically on popover open
- Filterable container list (All / Running / Stopped) with CPU and memory columns
- Live stats polling every 5 seconds while the window is visible
- Per-container detail panel with four tabs:
- Overview — image, state, ports, network
- Logs — last 100 lines with Refresh and Copy
- Inspect — raw JSON output, monospaced
- Stats — CPU % and memory
- Actions: Stop, Kill, Delete, Prune, Open Shell
- Register docker-compose files via a file picker; projects persist across launches
- Per-project and per-service Up, Down (stops containers, does not remove them), and Build actions
- Live per-service status derived from
container listby matching the<project>-<service>naming convention - Last command output panel for each project
- "Show Container" shortcut links compose services into the existing detail panel (logs, stats, inspect)
- Full-area install prompt with
brew install container-composeinstruction when the binary is missing
- Custom CLI path override (persisted via
@AppStorage) - Custom
container-composeCLI path override
The Xcode project is generated by XcodeGen and is gitignored. Regenerate it before opening in Xcode or after changing project.yml.
# Install XcodeGen if needed
brew install xcodegen
# Generate the project and build
xcodegen generate
xcodebuild -scheme ContainerApp -destination 'platform=macOS,arch=arm64' buildxcodebuild -scheme ContainerApp -destination 'platform=macOS,arch=arm64' testThe test suite includes:
- Unit tests — decoder tests against real CLI fixture output (
Fixtures/), stats merge logic, andTerminalLaunchershell-command generation. Always run. - Integration tests (
ContainerCLIRuntimeIntegrationTests) — skipped automatically when the CLI is absent or the system is stopped; creates and cleans up scratch containers when it runs.
Run a single suite:
xcodebuild -scheme ContainerApp -destination 'platform=macOS,arch=arm64' \
test -only-testing:ContainerAppTests/ContainerCLIModelsTestsApp Sandbox is disabled — the app must spawn the CLI and communicate with its XPC apiserver. Distribution is via Developer ID, not the Mac App Store. Opening a shell in a container triggers a one-time macOS Automation consent prompt (declared via NSAppleEventsUsageDescription).
View → ContainersViewModel → ContainerRuntime → container CLI
ContainerRuntime—Sendableprotocol; two implementations:ContainerCLIRuntime(real) andMockContainerRuntime(canned data for UI work without a live CLI).ContainerCLIRuntime— shells out viaProcessRunner. Discovers the binary in order: UserDefaults override →/usr/local/bin→/opt/homebrew/bin→which. Non-zero exits are mapped to typed errors (cliNotFound,systemNotRunning,commandFailed,decodingFailed); raw stderr is never shown in the UI.ComposeRuntime/ContainerComposeCLIRuntime— separate seam forcontainer-compose. Own binary discovery chain keyed bycontainerComposeCLIPathin UserDefaults.downis absent by design (see CLAUDE.md); stop/teardown usesContainerRuntime.stop(id:)on matched containers.ComposeFileParser— parses compose YAML with Yams (the app's first SwiftPM dependency) to get accurate service lists before firstup. Lenient: unknown keys ignored, null service bodies allowed.ComposeProjectStore— persists registered compose-file paths in UserDefaults (composeProjectPaths).ProcessRunner— wrapsFoundation.Process. Reads stdout and stderr concurrently to prevent pipe-buffer deadlocks; sets stdin to/dev/nullso interactive prompts fail fast.- DTO layer (
ContainerCLIModels.swift) — mirrors the CLI JSON with all-optional fields (tolerates unknown future keys), maps to flat UI models (ContainerSummary,ContainerStats). Container id doubles as name. - Stats —
container statsreports cumulativecpuUsageUsec; CPU % is derived from the delta between two samples inContainersViewModel.mergeStats. TerminalLauncher— opens a shell viaosascript → Terminal.app. Validates the container id against^[A-Za-z0-9._-]+$before interpolation (injection defense).
ContainerApp/
ContainerAppApp.swift # app entry point, scene declarations
Models/ # ContainerSummary, ContainerState, ContainerStats, …
Runtime/ # ContainerRuntime + ComposeRuntime protocols + CLI/mock implementations
ViewModels/ContainersViewModel.swift
Views/
MenuBar/ # popover views
Dashboard/ # window views (sidebar, table, detail panel, tabs, compose)
Shared/ # ErrorBannerView, EmptyStateView, state colours
Utilities/ # TerminalLauncher, ComposeFileParser, ComposeProjectStore
ContainerAppTests/ # Swift Testing suites
Fixtures/ # captured real CLI output (backs decoder tests)
project.yml # XcodeGen spec — edit this, not the .xcodeproj
- Edit
project.ymlfor build-setting changes or new targets; runxcodegen generateafterwards. - New source files under
ContainerApp/are picked up automatically by the target glob. - When updating the CLI version, re-capture fixture output and update
Fixtures/README.md. - Known non-blocking issues are tracked in
CURRENT_ISSUES.md.
ContainerApp is released under the MIT License.
ContainerApp is an independent, standalone UI application and is not affiliated with or endorsed by Apple. It interacts with Apple's container project (licensed under Apache License 2.0) solely by invoking the separately installed container CLI as an external process — no Apple container code is linked, embedded, or redistributed in this repository or in the built app, so its Apache 2.0 terms do not extend to this codebase. If a future release ever bundles the container binary inside the app, that release must ship Apple's Apache 2.0 license text and NOTICE file alongside it.