Architecture Overview
MultiDocker is two artefacts that share one engine:
- The
mdockerCLI — a bash entry point atbin/mdocker(~1900 lines), a shared library atlib/common.sh, and helper scripts underengine/andscripts/. - 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 throughProcess.
Everything the app can do, the CLI can do — the app is a front-end, not a fork.
The layered picture
Section titled “The layered picture”SwiftUI App ──► MdockerService ──► bin/mdocker ──► lib/common.sh- The app observes
@Publishedstate and sends actions to view models. MdockerServiceis the umbrella Swift service. It invokes the CLI with--json, decodes the output into models, and drives every view.bin/mdockeris the single dispatcher. Its bottomcase "$cmd"block routes each verb to acmd_*function.lib/common.shholds the shared helpers — config loading, the registry read/write functions, theCOMPOSE_FILEchain builder, and the port-conflict pre-flight.
Specialised work is delegated to focused scripts under engine/ and scripts/.
What reads what
Section titled “What reads what”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 chainedge/compose.yml ──► Traefik (provider=docker via docker-proxy) ──► project containers labelled via generated overrideTwo 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.
The three building blocks
Section titled “The three building blocks”| 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 |
The tech stack
Section titled “The tech stack”| 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/} |
Cookie settings
We only use statistics tools if you allow it. Necessary storage keeps this site working; everything else is off until you decide.
Stores your choice from this dialog in your browser (localStorage). No cookie, no transfer to third parties.
Google Analytics 4 via Google Tag Manager, to measure how the site is used. Loaded only after you allow it.