The single source of truth for my homelab. Infrastructure, configuration, and documentation as code — reconciled from Git, deployed by my own C#-native CI/CD.
This is an eat-your-own-dogfood repo: the lab is provisioned by a build engine I wrote (Fallout) driving API clients I wrote (ProxmoxSharp · UnifiSharp · SynoSharp). If it isn't in the repo, it doesn't exist.
Three Proxmox VE nodes + a Synology NAS behind a UniFi Cloud Gateway, fronted by
Cloudflare (cloudflared tunnels + Pangolin for public ingress). desktop-01 is the
heavy node — it sleeps when idle and is woken on demand (Wake-on-LAN).
graph TB
Internet(["🌐 Internet"]) --> CF["☁️ Cloudflare · chrison.dev<br/>(tunnels + Pangolin ingress)"]
subgraph NET["📡 UniFi · Cloud Gateway Ultra"]
direction LR
GW["🛡️ Gateway<br/>192.168.178.1"] --- SW["🔌 US-24-PoE"] --- AP["📶 U7LR APs"]
end
CF -. cloudflared .-> NET
subgraph PVE["🖥️ Proxmox VE cluster"]
direction LR
NUC["<b>nuc-01</b><br/>always-on · edge/control"]
HPE["<b>hpe-01</b><br/>always-on · media/home"]
DESK["<b>desktop-01</b><br/>Wake-on-LAN · dev/CI"]
end
NET ==> PVE
NAS[("🗄️ Synology DS1813+<br/>NFS datastore")]
NAS == NFS ==> PVE
classDef store fill:#fef3c7,stroke:#d97706;
classDef edge fill:#ede9fe,stroke:#7c3aed;
class NAS store;
class NET,GW,SW,AP edge;
VLANs: Homelab
10.10/16· Consumer10.20/16· IoT10.40/16· Network devices10.0/16· legacy192.168.178–179(being retired). Seedocs/Network.md.
There is no hand-written pipeline YAML for the logic. A commit triggers GitHub
Actions on a self-hosted runner (in the lab, for LAN reach), which runs the
Fallout build (./build.sh) — a C#
console app. Fallout drives the engine (Infrastructure/engine), which
reconciles declared shapes (stack.yaml) against live state via our own API
clients. PRs validate; merges/dispatches converge.
flowchart LR
Dev["💻 git push / PR"] --> GHA["⚙️ GitHub Actions"]
subgraph Runner["🏃 self-hosted runner · in-lab"]
direction TB
GHA -->|"pull_request"| VAL["validate-shapes.yml<br/>./build.sh ValidateShapes"]
GHA -->|"merge / dispatch"| DEP["deploy-stack.yml<br/>./build.sh Deploy --stack X"]
VAL --> FO1["🧰 Fallout build"]
DEP --> FO2["🧰 Fallout build"]
FO1 --> ENGV["engine · validate shapes"]
FO2 --> ENGC["engine · converge --apply"]
end
FEEDS["📦 nuget.org · Chrison.*<br/>+ Fallout-build feed"] -. restore .-> FO1
FEEDS -. restore .-> FO2
ENGC -->|ProxmoxSharp| PVE["🖥️ Proxmox VE<br/>LXCs / VMs"]
ENGC -->|UnifiSharp| UNI["📡 UniFi networks"]
ENGC -->|SynoSharp| NAS["🗄️ Synology NAS"]
classDef feed fill:#e0e7ff,stroke:#4f46e5;
class FEEDS feed;
./build.sh # default: ValidateShapes (validate all stacks)
./build.sh Preview --stack Core # dry-run converge for one stack
./build.sh Deploy --stack Core # live applyRequires the .NET 10 SDK (see global.json) and GITHUB_PACKAGES_PAT
for the Fallout-build feed; the *Sharp clients come from nuget.org (public). Git
guardrails (PR-only, no direct main push) are enforced client-side by a Husky.NET
pre-push hook.
Stacks are declared as stack.yaml shapes; domain stacks live in their own
Homelab.Stacks.* repos, composed here as submodules under stacks/
(meta-repo model, ADR-0008).
flowchart TB
subgraph NUC["🟢 nuc-01 · always-on"]
N1["Pangolin (ingress)"] --- N2["Traefik"] --- N3["cloudflared"] --- N4["Proxmox DC Manager"]
end
subgraph HPE["🟢 hpe-01 · always-on"]
H1["🎬 Media · Plex + arr"] --- H2["📊 monitoring"] --- H3["🏠 Home Assistant"] --- H4["📡 IoT · MQTT/Matter/ESPHome"]
end
subgraph DESK["🌙 desktop-01 · Wake-on-LAN"]
D1["Forgejo (+runner)"] --- D2["GitHub runners"] --- D3["ERP4FG"] --- D4["Topaz / Azure"]
end
classDef on fill:#dcfce7,stroke:#16a34a;
classDef wol fill:#e0e7ff,stroke:#6366f1;
class NUC,HPE on;
class DESK wol;
| Stack | Repo | Runs |
|---|---|---|
SmartHome |
Homelab.Stacks.SmartHome | IoT/home-automation support layer for HA (MQTT, Matter, ESPHome, Leapmotor Mate) |
BuildLab |
Homelab.Stacks.BuildLab | Windows 11 VM to test the Fallout build across VS toolchains |
Azure |
Homelab.Stacks.Azure | Local Azure (Topaz emulator) |
DevOps |
Homelab.Stacks.DevOps | Forgejo + runners, self-hosted DevOps |
Core · Media · monitoring · Gaming |
in-tree stacks/ |
Networking/edge · media fleet · Prometheus/Grafana · gaming VMs |
- IaC-first — declarative over imperative; UIs are a last resort.
- Idempotent — running the same thing twice changes nothing the second time.
- Reversible — changes roll back; destructive steps are explicit.
- Modular — small composable pieces; link and reuse, don't duplicate.
- Multi-everything — assume multiple nodes, multiple NAS, multiple services.
| Path | Purpose |
|---|---|
build/ |
The Fallout build project (Compile/ValidateShapes/Preview/Deploy). |
Infrastructure/ |
The C#-native engine (homelab-infra) + the shape schema + node bootstrap. |
stacks/ |
One stack per directory — mostly Homelab.Stacks.* submodules; each declares a stack.yaml. |
src/Proxmox/ |
Bash + PowerShell node-bootstrap / inventory scripts (.sh + .ps1 in sync). |
vendor/ |
Our own API clients as submodules, dogfooded — ProxmoxSharp / UnifiSharp / SynoSharp (consumed from nuget.org). |
docs/ |
Network, devices, services, ADRs, and plans. |
tools/PowerOrchestrator/ |
Demand-driven node sleep/wake (WoL) — powers desktop-01 down when idle. |
- Build system: Fallout — my C#/.NET build system (a NUKE successor). Build logic is code, not YAML.
- Engine:
Infrastructure/engine(homelab-infra) — validates + converges shapes. - API clients (dogfooded, on nuget.org):
Chrison.ProxmoxSharp·Chrison.UnifiSharp·Chrison.SynoSharp. - Ingress: Cloudflare tunnels + Pangolin.
- Decisions: see the ADRs (0001 IaC tooling → 0008 meta-repo model).
- Proxmox VE API · Synology DSM API
- Fallout — the build system behind all of this.