pbs-projects/Tech/Sessions/homelab-dns-decision.md

115 lines
4.2 KiB
Markdown

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