Skip to content

Home-Lab DR / Orchestration Blueprint

A disaster-recovery and orchestration blueprint for 4 home Windows 11 PCs, each running Docker Desktop with Linux containers. It covers two paths:

  • The recommended path — static addressing + Docker Compose in Git + hardened bespoke Postgres DR (compose/). This is what you should actually run.
  • The learning path — a real, runnable k3s cluster for practicing Kubernetes for your job (kubernetes/). Explicitly not recommended for production in this environment, and flagged as such throughout.

TL;DR of the verdict: For this homelab, full Kubernetes is overkill and fragile. Run Compose-in-Git plus a bespoke Postgres DR you actually test. Keep the GitHub-Actions self-hosted runner out of any cluster. Use k3s as a lab to learn k8s, not as your ops substrate. The full reasoning is in how-we-got-here.md.

The environment (constraints that drive every decision)

Constraint Consequence
4 home Windows 11 PCs, Docker Desktop, Linux containers Any Linux control plane runs inside WSL2/VMs — extra layers, extra failure modes.
No Linux servers, no always-on Linux node Nothing is reliably "the control plane". Nodes are workstations that sleep/reboot.
Home Wi-Fi, churning DHCP IPs Node addresses change under you. This breaks etcd/raft quorum and any IP-based DR reference.
One subnet is an isolated island Not all nodes can reach all nodes by default — clustering assumes full mesh.
Local disks only — no SAN/NAS/replicated storage No shared PersistentVolume. Stateful workloads can't just "reschedule" their data.
Services: claude-mem (stateful PG+Valkey+worker), a GH-Actions runner pool (already HA via GitHub), future stateless daemons Only claude-mem is genuinely stateful and DR-critical. The runner is already orchestrated by GitHub.

Services in scope

  • claude-mem — stateful: Postgres + Valkey + worker. DR today = hourly pg_dump warm standbys + failover scripts. This blueprint hardens that.
  • GitHub-Actions self-hosted runner — a warm pool of --ephemeral runners. GitHub is already the HA layer / scheduler. Keep it out of any cluster. One job needs a Windows host for DPAPI, so it can't move to a Linux pod anyway.
  • Future stateless dockerized daemons — the only workloads that would ever benefit from Swarm/k8s scheduling here, and only after the network is stable.

Decision matrix (scored against this environment)

Scoring: 5 = great fit here, 1 = poor fit here. These scores are environment-specific — they are not a general ranking of the technologies. Higher total = better fit for this homelab.

Criterion (weight) Compose-in-Git Docker Swarm k3s Full k8s (kubeadm) Bespoke DR scripts
Fit w/ Windows+Docker Desktop (x2) 5 4 2 1 5
Tolerates DHCP churn / subnet island (x3) 5 2 2 1 4
Handles STATEFUL Postgres DR (x3) 1 5
Ops burden / day-2 simplicity (x2) 5 3 2 1 3
Self-healing for STATELESS (x1) 4 5 5 1
Learning value for a k8s job (x1) 1 2 5 5 1
No always-on Linux node required (x2) 5 3 1 1 5
Weighted total (max 70) 53 31 31 21 57

Notes: 1. Compose alone doesn't replicate PG data — it hosts the DB; DR is the bespoke scripts' job. Scored on "doesn't fight you". 2. k3s/k8s can schedule a StatefulSet but do not replicate Postgres data; with local-disk-only storage a pod can't reschedule its data to another node. CloudNativePG helps but needs real storage + always-on nodes. 3. Compose gives per-node restart policies (restart: unless-stopped) — single-node self-healing, not cross-node rescheduling.

Read of the matrix: the recommended production substrate is Bespoke DR scripts + Compose-in-Git (top two totals). Swarm is the only cluster worth a later look, and only for stateless. k3s/full-k8s score low as production here but k3s wins on learning value, which is exactly why it lives in kubernetes/ as a lab.

Repository layout

homelab-dr-blueprint/
├─ README.md                  # top-level entry point: what/why/quickstart/security
├─ MIGRATION-PATH.md          # the ordered rollout: foundation -> compose -> DR -> [swarm] -> [k3s]
├─ TESTING.md                 # the 4-layer test matrix, honest about substitutions
├─ .env.example                # placeholders only; real .env is gitignored
├─ docs/
│  ├─ README.md               # (this file) overview + decision matrix
│  ├─ how-we-got-here.md      # the 3-seat council journey + unanimous verdict
│  ├─ pros-cons.md            # deep pros/cons per substrate + stateful/stateless + RPO/RTO
│  ├─ when-to-graduate.md     # concrete triggers for Swarm -> k3s -> full k8s
│  ├─ MAKING-OF.md            # living journal of how this repo came together
│  ├─ BOARD.md                # ticket-style board for work on this repo
│  ├─ technical/              # ARCHITECTURE.md (Mermaid) + SECURITY.md
│  └─ plain/                  # plain-language sibling of the root README
├─ foundation/                # static-DHCP guidance + stable node naming (hosts/Tailscale)
├─ compose/                   # RECOMMENDED path: compose stacks + bespoke Postgres DR + optional Swarm
│  ├─ scripts/                # deploy, backup, promote, restore-verify, primary-marker
│  ├─ stacks/                 # claude-mem (PG+Valkey+worker) + sample stateless daemon
│  └─ swarm/                  # optional Swarm variant (stateless only)
├─ kubernetes/                # LEARNING path: real k3s cluster, manifests, runbook, teardown
│  ├─ scripts/
│  └─ manifests/
├─ tests/                     # Pester 5+ suite: parse, hermetic exec, markdown links, manifests
├─ archive/                   # superseded files, never deleted (see archive/README.md)
└─ .github/workflows/ci.yml   # test + churn + security CI

Where to start

  1. Read how-we-got-here.md so the "why" is clear.
  2. Follow ../MIGRATION-PATH.md top to bottom.
  3. Do foundation/ first. Nothing else is stable until addressing is.

What is validated vs. what needs real hardware

  • Validated here: all PowerShell scripts parse cleanly (AST parse via System.Management.Automation.Language.Parser); YAML/manifest structure; doc cross-links. See each script header for its specific test status.
  • Needs real multi-machine hardware to fully test: Swarm join across nodes, k3s server+agent join, cross-node failover/promote timing, CloudNativePG on real storage, DHCP-reservation behavior on your specific router. These are marked in-script with # REQUIRES REAL HARDWARE banners.