From 06b836e34a63e7821e653755c7bf40e18438455d Mon Sep 17 00:00:00 2001 From: herbygitea Date: Wed, 6 May 2026 17:00:22 +0000 Subject: [PATCH] Create homelab-dns-decision.md via n8n --- Tech/Sessions/homelab-dns-decision.md | 115 ++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Tech/Sessions/homelab-dns-decision.md diff --git a/Tech/Sessions/homelab-dns-decision.md b/Tech/Sessions/homelab-dns-decision.md new file mode 100644 index 0000000..fd0b957 --- /dev/null +++ b/Tech/Sessions/homelab-dns-decision.md @@ -0,0 +1,115 @@ +--- +project: homelab-dns-decision +type: session-notes +status: active +path: Tech/Sessions +tags: + - homelab + - dns + - knot + - pihole + - unbound + - traefik +created: 2026-05-06 +updated: 2026-05-06 +--- +# Homelab DNS — Architecture Decision + +## Outcome + +Locked the internal DNS architecture. **Knot DNS** as a dedicated +authoritative server for `herbylab.dev`, running active/passive across +Proxmox and the NAS. Pi-hole + Unbound stays as the recursive frontend and +ad-blocker. + +## Architecture + + +Client → Pi-hole (ad-block + cache) + ├─ herbylab.dev queries → Knot (authoritative) + └─ everything else → Unbound → root servers + + +Split-horizon: `*.herbylab.dev` resolves internally to the Traefik VM IP +via Knot. Cloudflare remains public authoritative for the same zone. Same +name, different answer depending on who's asking. + +## Decisions + +| Decision | Choice | Rationale | +|---|---|---| +| Authoritative DNS | Knot DNS | Purpose-built authoritative, native +AXFR/NOTIFY, ~40MB RAM, zone files in plain text, `knotc` CLI for runtime +changes | +| Recursive + ad-block | Pi-hole + Unbound (kept) | Already works, no +reason to replace the recursive layer just because zone management is +clunky | +| Redundancy model | Active/passive, separate hosts | Knot primary on PVE, +secondary on NAS — separate failure domains | +| Replication | Native AXFR + NOTIFY | Edit primary, secondary auto-syncs. +No Ansible run per record change | +| Source of truth | Zone file in git | Versioned, debuggable, deployed to +primary via Ansible | +| Failover at client | OpenWrt DHCP hands out both Pi-hole IPs | +Active/passive at the resolver level. Adds the second Pi-hole to the +existing pattern | +| DHCP | Stays on OpenWrt | Knot doesn't touch DHCP | +| Internal certs | Wildcard `*.herbylab.dev` via Traefik + Cloudflare +DNS-01 | Real LE cert, no browser warnings, independent of internal DNS +layer. Works because Cloudflare owns the public zone — LE never touches the +internal IP | +| Zone structure | Flat (one zone for all `*.herbylab.dev`) | Single source +of truth; revisit if it gets unwieldy | + +## Why Not the Other Contenders + +- **Technitium** — right answer when *replacing* Pi-hole + Unbound +entirely. Overkill once Pi-hole stays. +- **PowerDNS** — sexy on paper (REST API, web UI option) but adds SQL +backend, second daemon for the UI, heavier replication setup. Can write a +custom GUI on top of Knot later if needed. +- **CoreDNS** — fine, but not really designed for primary/secondary AXFR. +Better fit for K8s-style deployments. +- **NSD** — close runner-up. Knot edged it on the `knotc` runtime CLI and +slightly nicer config syntax. +- **BIND9** — overkill, config-heavy. Skipped. + +## Key Learnings + +- **Internal certs are a naming problem, not a DNS problem.** Using ` +herbylab.dev` for internal names (split-horizon) means a public CA will +sign a wildcard cert via DNS-01. A `.lan` or `.internal` zone forces a +private CA + root cert distribution — not worth it. +- **The job defines the tool.** Original DNS contender list (Technitium, +CoreDNS, PowerDNS, BIND9) assumed a full Pi-hole + Unbound replacement. +Once Pi-hole stays, the job collapses to "small authoritative server, one +zone, easy to replicate" — different list (Knot, NSD, CoreDNS, PowerDNS). +- **Cloudflare, Tailscale, Traefik, internal DNS each do one thing.** +Cloudflare = public auth + ACME validation. Tailscale MagicDNS = device +naming on the tailnet. Traefik = service routing once a request lands. +Internal DNS = telling LAN clients where to send the packet. They don't +overlap, they compose. + +## Open Items + +- [ ] Stand up second Pi-hole instance for redundancy (parallel to Knot +work, not blocking) +- [ ] Decide Pi-hole sync method (gravity-sync vs Teleporter) +- [ ] Pick deployment unit for Knot on each host (LXC on PVE likely; Docker +on NAS) +- [ ] Initial zone file content — inventory current `*.herbylab.dev` +records to migrate from Pi-hole local DNS +- [ ] TTL strategy — start at 300 during buildout, raise to 3600 once stable +- [ ] Ansible role for Knot primary deployment +- [ ] Confirm Pi-hole conditional forwarding config for `herbylab.dev` → +Knot + +## Next Session + +Return to the Traefik chat with this decision locked. DNS slot in the +broader stack is now filled. + + + + +...sent from Jenny & Travis \ No newline at end of file