Skip to content

Architecture Overview

MultiDocker is two artefacts that share one engine:

  1. The mdocker CLI — a bash entry point at bin/mdocker (~1900 lines), a shared library at lib/common.sh, and helper scripts under engine/ and scripts/.
  2. MultiDocker.app — a native SwiftUI app (macOS 13+) under macos-app/. It never re-implements any logic; it wraps the CLI and talks to it through Process.

Everything the app can do, the CLI can do — the app is a front-end, not a fork.

SwiftUI App ──► MdockerService ──► bin/mdocker ──► lib/common.sh
  • The app observes @Published state and sends actions to view models.
  • MdockerService is the umbrella Swift service. It invokes the CLI with --json, decodes the output into models, and drives every view.
  • bin/mdocker is the single dispatcher. Its bottom case "$cmd" block routes each verb to a cmd_* function.
  • lib/common.sh holds the shared helpers — config loading, the registry read/write functions, the COMPOSE_FILE chain builder, and the port-conflict pre-flight.

Specialised work is delegated to focused scripts under engine/ and scripts/.

This is the canonical map of how control flows from a verb down to the tools that do the work:

SwiftUI App ──► MdockerService ──► bin/mdocker ──► lib/common.sh
├─► engine/detect.sh
├─► engine/render.sh ──► engine/profiles/*.yml
├─► engine/patch-proxy.sh
├─► engine/terminal.sh ──► tmux
├─► engine/health.sh
├─► engine/stats.sh
├─► engine/git.sh ──► git -C <project-path>
├─► engine/share.sh ──► cloudflared (nohup daemon)
├─► engine/shopware/*.sh ──► docker compose exec shop
├─► engine/db/*.sh ──► docker compose exec <db>
└─► scripts/{setup-tls,setup-dns,setup-network,doctor}.sh
bin/mdocker (any verb) ──► docker compose -p <name> ── using COMPOSE_FILE chain
edge/compose.yml ──► Traefik (provider=docker via docker-proxy) ──► project containers
labelled via generated override

Two independent flows meet at runtime: every project verb ends in a docker compose -p <name> call built from the COMPOSE_FILE chain, while the edge stack runs Traefik, which discovers those same containers through the labels that the generated override attached to them.

Block What it is Deep dive
Edge stack One central Traefik (plus Mailcatcher, dnsmasq and a docker-socket-proxy) on the shared multi-docker-edge network that routes <host>.<base-domain> to your containers. The Edge Stack
Override engine Per-project generated <name>.override.yml files that strip host ports and add Traefik labels — your original compose files are never touched. The Override Engine
mdocker CLI The bash entry point that ties it together by invoking docker compose with a COMPOSE_FILE chain. CLI Overview
Layer Technology
CLI bash 4+, yq (mikefarah v4), jq, docker compose v2, mkcert, tmux, lsof
Edge stack Traefik v3.5, dockage/mailcatcher 0.9, dnsmasq 2.90, nginx 1.30 (docker-socket-proxy)
App Swift 5.9+, SwiftUI, Combine, AppKit, SwiftTerm (terminal attach)
State (CLI) ~/.config/multi-docker/{config.env, registry.json, overrides/, certs/, snapshots/, state/}
State (App) ~/.multidocker/{bin/, runtime/<version>/, config/, preferences/, logs/, state/}