I'm the developer of DockZ, a free, Apache-2.0 Docker app for Apple Silicon Macs. Two things pushed me to build it:
- Licensing. Docker Desktop needs a paid subscription for commercial use at larger companies. I wanted something I could use anywhere without thinking about seats or license terms.
- Weight. Docker Desktop felt heavy on my Mac — a ~1.5 GB app and a lot of background activity just to run a few containers. I wanted something much lighter that still has a real GUI, not just a CLI.
The result is a ~8 MB SwiftUI app (3.5 MB DMG). This post is about how it works under the hood and a few problems I didn't expect.
The architecture in one picture
macOS Alpine VM (Virtualization.framework)
┌─────────────────────────────┐ ┌───────────────────────────────┐
│ DockZ (SwiftUI) │ vsock │ dockerd (upstream, unmodified)│
│ ├─ dashboard / monitor │◀────────▶│ containerd, BuildKit │
│ ├─ port forwarder │ │ │
│ └─ docker context "dockz" │ └───────────────────────────────┘
└─────────────────────────────┘
▲
│ docker / docker compose / buildx (unchanged)
-
No fork of Docker. The VM runs the real upstream
dockerd. DockZ registers a normal Docker context calleddockz, sodocker, Compose and BuildKit/buildx work exactly as they do anywhere else. - vsock instead of a virtual network for control traffic. The dashboard talks to the Docker Engine API over virtio-vsock — plain HTTP/1.1 per stream, no TCP port exposed on the Mac.
-
Automatic port forwarding. DockZ watches Docker's events API; when a container publishes a port, it's mirrored to the same port on the Mac and bound to the address Docker reports (so the default
0.0.0.0really is reachable from your LAN).
Because the heavy lifting is done by Apple's hypervisor and a stock Linux userland, the app itself stays small: no bundled Electron runtime, and the guest image is minimal Alpine.
Managing remote Docker engines — with keys that never leave the chip
The same window can manage other Docker engines: a server over SSH, a server over mutual TLS, or another engine's socket on the same Mac (Colima, OrbStack, Docker Desktop).
SSH was the easy part: DockZ runs docker system dial-stdio on the remote host and speaks the Engine API over that pipe.
TLS was more interesting. The usual setup leaves a key.pem sitting in ~/.docker — if someone gets your Mac's files, they get root-equivalent access to your servers. I wanted the client key to be unextractable:
- The private key is created inside the Mac's Secure Enclave (CryptoKit's
SecureEnclave.P256.Signing.PrivateKey), protected with Touch ID / user presence. - DockZ builds a PKCS#10 certificate signing request from it; only the CSR leaves the Mac. You sign it with your CA and paste the certificate back.
- Apple's Network.framework only accepts Keychain identities for TLS client auth, so DockZ uses swift-nio-ssl with a custom signing callback: during the handshake, NIO asks DockZ to sign, and the Secure Enclave does it.
- The
dockerCLI can't use a Secure Enclave key, so DockZ exposes a local relay socket per environment. The CLI talks plain HTTP to the socket; DockZ adds the TLS. The CLI never sees a key.
The session unlocks with Touch ID and relocks when the screen locks, the Mac sleeps, or you switch away.
One gotcha worth sharing: docker exec and attach rely on TCP half-close — the client closes its write side and keeps reading. My first relay closed both directions as soon as one side finished, which silently truncated output. Propagating half-close correctly (allowing remote half-closure, then closing only the output side after pending writes flush) fixed it.
When containers "disappeared": making the VM heal itself
Early on I hit a nasty one: every few days, all containers seemed to vanish from the dashboard. The console log showed the guest kernel had hit an oops in the virtio-vsock transmit path, after which ext4 deadlocked and dockerd stopped answering — the VM was technically "running" but useless, sometimes for hours.
I still haven't pinned down whether the root cause is in the guest driver or the host side. So DockZ now treats the VM like a supervised service:
-
Fail loudly instead of hanging. The guest runs with
panic_on_oops=1,softlockup_panic=1andpanic=10, so a broken kernel reboots instead of lingering half-alive. -
Health-check the engine. DockZ pings
/_pingevery 15 seconds; after 4 failures spanning at least 60 seconds of uptime (so a sleeping Mac doesn't count), the engine is declared unresponsive and the VM is restarted. - Crash-loop protection. At most 3 automatic restarts per 10 minutes; after that DockZ stops and tells you instead of looping forever.
- Keep the evidence. Console logs of the last 5 boots are rotated and kept, so the next oops can actually be investigated.
I also cut down vsock churn: the Monitor tab used to poll container stats, opening a new connection per container every few seconds. It now keeps one long-lived stats stream per container, and the dashboard refreshes on Docker events instead of polling every 4 seconds.
A small bug that crashed the app: Docker's -1
The Monitor tab shows disk usage per image, volume and build cache. Docker reports -1 when a size is unknown. Decoding that into an unsigned integer wrapped it to UInt64.max, and the arithmetic downstream crashed. Now every byte count from Docker goes through one helper that turns negative values into "unknown". Small lesson: when you consume someone else's JSON, sentinel values deserve a single choke point, not scattered checks.
What else is in there
- A native dashboard: containers grouped by Compose stack with crashes and failing health checks flagged, images, volumes, networks, registries, live logs, and filters plus search on every list.
- A Monitor tab with live CPU, memory, network and disk per container, VM vitals, and cleanup of unused images, volumes and build cache.
- Multipass-style Linux machines (Alpine/Debian/Ubuntu), one-click multi-node k3s/kubeadm templates, and APFS copy-on-write snapshots of VM disks.
Limitations (honestly)
- Apple Silicon and macOS 15+ only.
- It's a young project.
- Releases aren't notarized yet, so macOS asks you to allow the app once (System Settings → Privacy & Security → Open Anyway).
Try it
brew tap nextage-soft/dockz https://github.com/nextage-soft/dockz
brew install --cask nextage-soft/dockz/dockz
Or download the DMG from GitHub Releases.
- GitHub: https://github.com/nextage-soft/dockz
- Website (with comparisons against Docker Desktop, OrbStack and Colima): https://dockz.nextagesoft.com
It's actively under development. If you use Docker Desktop, OrbStack or Colima daily, I'd really like to hear what feels broken, missing or unnecessarily complicated — and if you've seen vsock trouble on Virtualization.framework, I'd love to compare notes in the comments.
Top comments (0)