--- 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