# Mocker
**A Docker-compatible container management tool built on Apple's Containerization framework**
[](https://swift.org)
[](https://developer.apple.com/macos/)
[](LICENSE)
[](Package.swift)
[](https://github.com/sponsors/us)
[English](README.md) · [简体中文](README.zh-CN.md)
If Mocker saves you from Docker Desktop, [**sponsor the project on GitHub**](https://github.com/sponsors/us) — it directly funds development.
---
Mocker is a **Docker-compatible CLI + Compose** tool that runs natively on macOS using Apple's [Containerization](https://developer.apple.com/documentation/containerization) framework (macOS 26+). It speaks the same language as Docker — same commands, same flags, same output format — so your existing scripts and muscle memory just work.
## Just replace `docker` with `mocker`
```bash
# Before
docker compose up -d
docker ps
docker logs my-app
docker exec -it my-app sh
# After — same commands, native Apple runtime, no Docker Desktop
mocker compose up -d
mocker ps
mocker logs my-app
mocker exec -it my-app sh
```
Your existing `docker-compose.yml` works as-is.
## What's New
See the **[CHANGELOG](CHANGELOG.md)** for the full, always-current release history — it is regenerated automatically on every release.
## Features
- **Docker CLI compatible** — `run`, `ps`, `stop`, `rm`, `exec`, `logs`, `build`, `pull`, `push`, `images`, `tag`, `rmi`, `inspect`, `stats`
- **Network management** — `network create/ls/rm/inspect/connect/disconnect`
- **Volume management** — `volume create/ls/rm/inspect`
- **Docker Compose v2** — `compose up/down/ps/logs/restart` with dependency ordering
- **MenuBar GUI** — Native SwiftUI app *(coming soon)*
- **JSON state persistence** — All metadata stored in `~/.mocker/`
- **Swift 6 concurrency** — Full actor-based thread safety throughout
> **Compatibility note:** Mocker parses all Docker CLI flags for drop-in compatibility, but some flags are not supported by Apple's Containerization runtime and will produce a warning or error. See [COMMANDS.md](COMMANDS.md) for details on which commands are fully functional vs unsupported.
## Requirements
| Component | Version |
|-----------|---------|
| macOS | 26.0+ (Sequoia) |
| Swift | 6.0+ |
| Xcode | 16.0+ |
> **Note:** The Apple Containerization framework requires macOS 26 on Apple Silicon. Intel Macs are not supported.
## Installation
### Homebrew (Recommended)
```bash
brew tap us/tap
brew install mocker
```
### Build from Source
```bash
git clone https://github.com/us/mocker.git
cd mocker
swift build -c release
cp .build/release/mocker /usr/local/bin/mocker
```
## Quick Start
```bash
# Pull an image
mocker pull nginx:1.25
# Run a container
mocker run -d --name webserver -p 8080:80 nginx:1.25
# List running containers
mocker ps
# View logs
mocker logs webserver
# Stop and remove
mocker stop webserver
mocker rm webserver
```
## Usage
### Container Lifecycle
```bash
# Run with environment variables and volumes
mocker run -d \
--name myapp \
-p 8080:80 \
-e APP_ENV=production \
--env-file .env \
-v /host/data:/app/data \
myimage:latest
# Run interactively (foreground)
mocker run --name temp alpine:latest
# Force remove a running container
mocker rm -f myapp
# Execute a command inside a running container
mocker exec myapp env
# Follow logs
mocker logs -f myapp
```
### Images
```bash
# Pull specific tag
mocker pull postgres:15
# List images
mocker images
# List image IDs only
mocker images -q
# Tag an image
mocker tag alpine:latest my-registry.io/alpine:v1
# Remove an image
mocker rmi my-registry.io/alpine:v1
# Build from Dockerfile
mocker build -t myapp:latest .
# Multi-platform build (repeats --platform per architecture)
mocker build --platform linux/amd64 --platform linux/arm64 -t myapp:latest .
# Push to registry
mocker push my-registry.io/myapp:latest
```
### Manifest Lists (multi-arch images)
```bash
# Inspect an OCI image index
mocker manifest inspect myrepo/multi:latest
# Assemble a manifest list from existing per-arch images
mocker manifest create myrepo/multi:latest myrepo/app:amd64 myrepo/app:arm64
# Splice a child image's platform into an existing list (replaces same-platform entry)
mocker manifest add myrepo/multi:latest myrepo/app:arm64
# Drop an entry by platform spec or digest
mocker manifest rm myrepo/multi:latest linux/amd64
mocker manifest rm myrepo/multi:latest sha256:cb96058800ca…
# Override platform metadata on an entry
mocker manifest annotate myrepo/multi:latest myrepo/app:arm64 --variant v8
# Push the assembled list to the registry
mocker manifest push myrepo/multi:latest
```
### Building for exotic architectures
`mocker build --platform linux/ppc64le|s390x|riscv64` works for layer-only Dockerfiles
(`FROM` / `COPY` / `CMD`) but fails with **`Exec format error`** on any `RUN` instruction.
Apple's `container build` BuildKit VM is an arm64 Linux VM with **no QEMU `binfmt_misc`
handlers** for non-arm64/non-amd64 architectures. `linux/amd64` works only because Apple
Silicon ships hardware Rosetta 2 translation. Tracking upstream:
[apple/container#1496](https://github.com/apple/container/issues/1496).
Until Apple ships QEMU support, three workarounds exist:
| Path | Tradeoff |
|------|----------|
| **Remote builder.** Point mocker at a Linux host that already has QEMU `binfmt` registered: `mocker build --builder --platform linux/ppc64le …`. The `--builder` value is forwarded to `container build --builder`, which can target a remote BuildKit node (e.g. one registered via `docker buildx create --driver remote ssh://host`). The `RUN` steps then execute on the remote host's emulation, not the local arm64 VM. | Needs a reachable remote Linux host (native ppc64le, or x86/arm with `qemu-user-static`) and a one-time builder registration. |
| **Run a Podman machine** alongside mocker. Its Fedora CoreOS VM has `qemu-user-static` registered, so `podman build --platform linux/ppc64le` handles `RUN` steps. Use `mocker manifest create` afterwards to assemble per-arch images into a list. | Requires a persistent QEMU VM — extra memory and reliability surface. |
| **`container run --virtualization`** a Linux VM, install `qemu-user-static` + Docker inside, build there, then `container image save` / `mocker manifest add` the result. | Manual setup; one-time per arch you need. |
For arm64 and amd64 (Rosetta 2) the native path is faster and supported — exotic-arch
emulation is a workaround until upstream lands.
### Inspect & Stats
```bash
# Inspect container (JSON output)
mocker inspect myapp
# Inspect multiple targets, or constrain discovery by type
mocker inspect container1 container2 alpine:latest
mocker inspect --type image alpine:latest
# Docker-compatible ImageInspect JSON array; select a platform when needed
mocker image inspect --platform linux/amd64 alpine:latest
# Resource usage stats
mocker stats --no-stream
```
### Networks
```bash
# Create a network
mocker network create mynet
# List networks
mocker network ls
# Connect a container
mocker network connect mynet myapp
# Disconnect
mocker network disconnect mynet myapp
# Inspect
mocker network inspect mynet
# Remove
mocker network rm mynet
```
### Volumes
```bash
# Create a named volume
mocker volume create pgdata
# List volumes
mocker volume ls
# Inspect (shows mountpoint)
mocker volume inspect pgdata
# Remove
mocker volume rm pgdata
```
### Docker Compose
```bash
# Start all services (detached)
mocker compose -f docker-compose.yml up -d
# List compose containers
mocker compose -f docker-compose.yml ps
# View logs for a service
mocker compose -f docker-compose.yml logs web
# Restart a service
mocker compose -f docker-compose.yml restart api
# Tear down
mocker compose -f docker-compose.yml down
```
Example `docker-compose.yml`:
```yaml
version: "3.8"
services:
web:
image: nginx:1.25
ports:
- "8080:80"
depends_on:
- api
api:
image: myapp:latest
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
```
### System
```bash
# System information
mocker system info
# Remove stopped containers and unused resources
mocker system prune -f
```
## Architecture
```
mocker/
├── Sources/
│ ├── MockerKit/ # Shared core library
│ │ ├── Models/ # Data types (ContainerInfo, ImageInfo, ...)
│ │ ├── Config/ # MockerConfig (~/.mocker/ paths)
│ │ ├── Container/ # ContainerEngine + ContainerStore (actor)
│ │ ├── Image/ # ImageManager + ImageStore (actor)
│ │ ├── Network/ # NetworkManager (actor)
│ │ ├── Volume/ # VolumeManager (actor)
│ │ └── Compose/ # ComposeFile parser + ComposeOrchestrator
│ ├── Mocker/ # CLI executable
│ │ ├── Commands/ # One file per command group
│ │ └── Formatters/ # TableFormatter, JSON output
│ └── MockerApp/ # SwiftUI MenuBar app (macOS 26+)
│ ├── MenuBar/
│ ├── ViewModels/
│ └── Views/
└── Tests/
├── MockerKitTests/ # Unit tests for core library
└── MockerTests/ # CLI integration tests
```
### Key Design Decisions
| Concern | Approach |
|---------|----------|
| Thread safety | All engines/managers are `actor` types |
| Persistence | JSON files in `~/.mocker/{containers,images,networks,volumes}/` |
| CLI parsing | `swift-argument-parser` with `AsyncParsableCommand` |
| YAML parsing | `Yams` library |
| Compose naming | Docker v2 convention: `projectName-serviceName-1` (hyphen separator) |
| JSON output | Always wrapped in array `[{...}]`, matching Docker's `inspect` format |
## Data Directory
Mocker stores all state in `~/.mocker/`:
```
~/.mocker/
├── containers/ # Container metadata (one JSON file per container)
├── images/ # Image metadata
├── networks/ # Network metadata
└── volumes/ # Volume metadata + actual data directories
└── pgdata/
└── _data/
```
## Docker Compatibility
Mocker aims for full CLI compatibility with Docker. Key behaviors matched:
- Error messages: `Error response from daemon: ...`
- `inspect` always returns a JSON array, even for a single object
- `pull` idempotency: re-pulling an existing image shows "Image is up to date"
- Compose container naming: `project-service-1` (hyphen, not underscore)
- `stop` and `rm` echo back the identifier provided by the user
- Short IDs are 12 characters (first 12 of full 64-char hex ID)
## Building & Testing
```bash
# Build all targets
swift build
# Run all tests
swift test
# Run specific test suite
swift test --filter MockerKitTests
# Run CLI directly
swift run mocker --help
```
## Performance
Benchmarks run on Apple M-series, macOS 26 (`hyperfine --warmup 5 --runs 15`):
| Tool | Container startup | vs Docker |
|------|:-----------------:|:---------:|
| Docker Desktop | 320 ms | baseline |
| Apple `container` CLI | 1,030 ms | 3.2× slower |
| **Mocker** | **1,153 ms** | **3.6× slower** |
Apple's VM-per-container model trades startup time for stronger isolation — every container gets its own lightweight Linux VM. Mocker adds only ~120 ms of management overhead on top of Apple's runtime.
**CPU & Memory throughput** (sysbench inside container, 30s run):
| Metric | Docker | Apple Container |
|--------|:------:|:---------------:|
| CPU events/s | 7,958 | 7,894 |
| Memory throughput | 13,340 MiB/s | 13,119 MiB/s |
Raw compute performance is equivalent — the VM boundary has negligible overhead for CPU and memory workloads.
## How It Works
Mocker delegates to Apple's `container` CLI for container lifecycle (run, stop, exec, logs, build).
Image operations (pull, list, tag, rmi) use `Containerization.ImageStore` directly. This hybrid
approach gives you a fully working Docker-compatible tool on macOS 26 today:
| Operation | Backend |
|-----------|---------|
| `run`, `stop`, `exec`, `logs` | `/usr/local/bin/container` subprocess |
| `build` | `container build` with live streaming output |
| `pull`, `push`, `tag`, `rmi` | `Containerization.ImageStore` (direct framework) |
| `images` | Apple CLI image store (shows all pulled + built images) |
| `stats` | VM process RSS/CPU via `ps` (VirtualMachine.xpc matching) |
| Port mapping `-p` | Persistent `mocker __proxy` subprocess per port |
## Roadmap
- [x] Full Docker CLI flag compatibility (111 commands)
- [x] Docker Compose v2 support
- [x] Network & Volume management
- [ ] MenuBar GUI
- [x] Real container execution on macOS 26 (via Apple `container` CLI)
- [x] `mocker build` — delegates to `container build` with live output
- [x] `mocker stats` — real CPU% and memory from VM process
- [x] Port mapping (`-p`) — userspace TCP proxy subprocess
- [x] Registry authentication (`mocker login` / `mocker logout`, keychain-backed)
- [ ] `mocker compose --scale`
- [ ] MenuBar live container metrics (CPU, memory, logs)
- [ ] Image layer size reporting
- [ ] Direct Containerization framework integration (pending vminit compatibility)
## Contributing
Contributions are welcome! Please read [docs/contributing.md](docs/contributing.md) for guidelines.
```bash
# Fork and clone
git clone https://github.com/yourname/mocker.git
# Create a feature branch
git checkout -b feat/my-feature
# Make changes and test
swift test
# Commit with Conventional Commits
git commit -m "feat: add my feature"
```
## License
AGPL-3.0 — see [LICENSE](LICENSE) for details.
---
Built with Swift on macOS · Powered by Apple Containerization