4.2 KiB
4.2 KiB
| project | type | status | path | tags | created | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| homelab-dns-decision | session-notes | active | Tech/Sessions |
|
2026-05-06 | 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
knotcruntime 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.devfor internal names (split-horizon) means a public CA will sign a wildcard cert via DNS-01. A.lanor.internalzone 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.devrecords 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