Migration Path¶
The one-page sequence. Do these in order. Do not skip ahead — every later stage assumes the earlier ones are done. Each stage lists its entry gate (what must be true before you start) and its done signal.
[0] Foundation -> [1] Compose-in-Git -> [2] Hardened Postgres DR
(static IPs, (services declared (pg_dump warm-standby +
stable names) in git, health- tested promote +
checked, restart) restore-verify)
|
v
[3] (OPTIONAL) Swarm for STATELESS only
|
v
[4] (LATER / LEARNING) k3s
Stage 0 — Foundation (do this first, always)¶
Why first: every clustering tech (Swarm, k3s, full k8s) and even the DR
failover scripts assume nodes can find each other by a name that does not
change. Home Wi-Fi hands out churning DHCP leases and one subnet here is an
isolated island. That instability is exactly what breaks etcd quorum, Swarm raft,
and any PRIMARY_HOST reference. Fix addressing before anything else.
- Do:
foundation/Set-HomelabHostsAliases.ps1to pin stable names, readfoundation/README.mdfor DHCP-reservation guidance, optionally install Tailscale for MagicDNS (survives subnet islands + IP churn). - Entry gate: none.
- Done when: every node resolves every other node by a stable name that does
not change across reboots/lease renewals (
ping homelab-node2works from all nodes, including across the isolated subnet if you took the Tailscale option).
Stage 1 — Compose-in-Git (the recommended baseline)¶
Why: declarative, diffable, trivially restartable, no control plane to keep quorate. This is the pragmatic substrate the council endorsed.
- Do:
compose/scripts/Deploy-ComposeStack.ps1for each stack; commit the compose files. Health checks +restart: unless-stoppedgive you single-node self-healing without a scheduler. - Entry gate: Stage 0 done.
- Done when: claude-mem stack + sample daemon come up healthy from a clean
git cloneon any node with one script call.
Stage 2 — Hardened Postgres DR¶
Why: no orchestrator (Swarm, k3s, k8s) replicates your Postgres data for you. Statefulness is solved at the database layer, deliberately, or not at all.
- Do: schedule
compose/scripts/Backup-PostgresWarmStandby.ps1hourly; keepPromote-PostgresStandby.ps1andTest-PostgresRestore.ps1tested; use the primary-marker (Set-PrimaryMarker.ps1) so two nodes never both think they are primary. - Entry gate: Stage 1 done.
- Done when: a scheduled restore-verification passes on a standby, and a rehearsed promote brings claude-mem back on a second node within your RTO.
Stage 3 — (OPTIONAL) Swarm for stateless only¶
Why optional: Swarm is the only clustering worth a look here, and only after the network is genuinely stable, and only for stateless daemons. Never put Postgres in it.
- Do:
compose/swarm/Init-Swarm.ps1+Deploy-SwarmStack.ps1. - Entry gate: Stage 0 rock-solid for weeks (no name/IP churn incidents), Stage 1+2 done, and you actually have 2+ stateless daemons worth spreading.
- Done when: a stateless service survives a node drain by rescheduling.
- See:
docs/when-to-graduate.md.
Stage 4 — (LATER / LEARNING) k3s¶
Why last: full/real k8s here is overkill and fragile (Linux control plane
on Windows via WSL2/VMs, Wi-Fi/DHCP churn breaks etcd quorum, k8s does not
replicate Postgres). But it is the best hands-on way to practice k8s for a
job. Treat kubernetes/ as a lab, not production.
- Do:
kubernetes/— stand up k3s on Linux nodes (WSL2/VMs), run the practice runbook, tear it down. - Entry gate: you want k8s practice, OR the graduation triggers in
docs/when-to-graduate.mdfire (e.g. you own an always-on Linux mini-PC with a static IP). - Done when (learning): you can create → deploy → kill a node → watch reschedule → teardown from the runbook unaided.
See docs/README.md for the scored decision matrix and docs/how-we-got-here.md
for the council reasoning behind this ordering.