Skip to main content

Local development

The same compose files production uses will run the whole stack on a Mac. Docker Desktop must be running. Nothing else is required — no SOPS, no age key, no Tailscale, no VPS access, no secrets of any kind. .env.local is created for you from .env.local.example on first run.
That builds and starts every stack — edge, observability, vault, database and identity — and waits until they actually answer.

Verified from a clean tree

The local runbook is verified by running it, not by review — a fresh clone into an empty directory, every command exactly as written, nothing outside the runbook needed. It was first verified on 2026-07-26 against main at 0b40403, fully cold: every stack image deleted and an empty build cache. That run predated the database and identity stacks, so its figures went stale as soon as Postgres and Keycloak were added. It was re-measured on 2026-07-27 and the numbers below are from that run.
Re-measured 2026-07-27 against main at 4ee21c0. The figures below are current.What the run covered: a fresh git clone into an empty directory, no .env.local (copied from .env.local.example as the runbook says), no pre-existing volumes, networks or containers, and isolated prefixes and ports so it could not reuse anything from an existing stack. What it did not cover: the image cache was left in place, because the images are shared with unrelated projects on the same machine. So the timings are warm-cache — a genuinely first-ever run that pulls every image will be slower, and how much slower depends on your connection rather than on this stack.
Thirteen containers is main for the homelab alone. The AI application now runs as a tenant of this stack and adds eleven more, so a plain host count of twenty-four is expected when both are up — see AI Application. If you see more than that, check what else is running — a worktree on a feature branch will bring up its own services under the same compose project names. During this verification a MinIO container was running locally from an unmerged branch, which is why a naive count on the host said fifteen. Start is roughly a minute, most of it Grafana installing plugins. The point of those commands is that the result is re-verifiable at any time — run them and check.

Source documents