wiki-vault/Sources/Homelab/dns-knot-pihole-end-to-end.md
Travis Herbranson 34a268d8dc migration: copy 62 notes from pbs-projects and homelab-projects
Two-layer structure: Sources (raw notes) + Wiki (compile output)
Four domains: Dev (40), Venture (3), Homelab (23), Reference (0)
Includes CLAUDE.md spec, index pages at all levels, compile log

Co-Authored-By: Lovebug <lovebug@herbylab.dev>
2026-05-08 17:21:13 -04:00

4.3 KiB

created path project status tags type updated
2026-05-08 Sources/Homelab dns-knot-pihole-end-to-end completed
homelab
dns
knot
pihole
openwrt
traefik
session-notes 2026-05-08

DNS — Knot + Pi-hole End-to-End

Outcome

Phase 1 of the Traefik project closed. Internal DNS architecture from the homelab DNS decision doc is live: Knot DNS authoritative for herbylab.dev on LXC 103, Pi-hole conditionally forwarding the zone, OpenWrt passing answers through. All six initial records resolve cleanly from a normal LAN client through the full chain.

Architecture (as built)

Client → OpenWrt (10.0.11.1) → Pi-hole (10.0.11.50) ├─ herbylab.dev → Knot (10.0.11.10) └─ everything else → Unbound → root

Records served

Name IP VLAN
ns1 10.0.11.10 Lab
immich 10.0.11.62 Lab
pihole 10.0.11.50 Lab
plex 10.0.11.54 Lab
pve 10.0.21.188 Private
traefik 10.0.21.181 Private

Migrated from .lan to .dev to enable LE wildcard via Cloudflare DNS-01 in Phase 2.

Key Learnings

  • knotc reload does not re-bind sockets. It reloads the config file but listening sockets only get re-evaluated on full daemon restart. If listen: directives change (or were missing on first start), systemctl restart knot is required. Symptom: zone loads, zone-status reports correctly, but dig returns "connection refused."
  • Knot config order matters for references. acl blocks must be defined before zone blocks that reference them — YAML validation is single-pass, no forward references. Same pattern likely applies to other ID-based references.
  • Fresh PVE LXCs inherit Tailscale resolvers from the host. /etc/resolv.conf gets populated with 100.100.100.100 by default. If the LXC isn't on the tailnet, name resolution silently fails until pointed at a reachable resolver. Permanent fix: configure DNS in the LXC's PVE config or, for a DNS server LXC, point it at itself once it's serving.
  • OpenWrt rebind protection silently filters RFC1918 answers. When dnsmasq has rebind_protection='1', any DNS answer pointing to a private IP gets stripped — query returns NOERROR with ANSWER:0 and EDE 15 (Blocked). The fix is whitelisting the domain via rebind_domain. Easy to miss because the block happens after the upstream answers correctly, so direct queries to Pi-hole or Knot work fine while client-path queries fail.
  • EDE 15 (Blocked) doesn't always mean a blocklist match. It's a generic "this answer was filtered" code. Pi-hole's blocklists, OpenWrt's rebind protection, and several other filters all use it. Pi-hole's query log saying "Allowed" while dig reports EDE 15 is the tell — block is happening downstream of Pi-hole.
  • Pi-hole v6 renamed conditional forwarding. It's now under reverse-server config with format ,,,. Same plumbing, different UI surface.
  • Direct-vs-chain queries are the diagnostic shortcut. When a DNS chain fails, querying each link directly with dig @ immediately localizes the broken hop. Saved real time tonight isolating the OpenWrt rebind issue.

Decisions Locked

  • Zone file lives at /var/lib/knot/herbylab.dev.zone on LXC 103, owned by knot:knot
  • Single NS record (ns1) for now; secondary will be added when NAS instance comes up
  • TTL strategy: 300 during buildout (current), raise to 3600 once stable
  • SOA serial format: YYYYMMDDNN (current: 2026050801)
  • ACL allows transfer/notify from localhost only — will expand when secondary is added
  • Conditional forwarding scoped to 10.0.0.0/8 in Pi-hole — VLAN/firewall layer enforces actual access boundaries

Open Items

  • LXC /etc/resolv.conf is a manual edit pointing at Pi-hole — repoint at itself (10.0.11.10) with Pi-hole as fallback, make persistent
  • Knot zone file into git as source of truth
  • Ansible role for Knot primary deployment
  • Knot secondary on the NAS (after primary proven stable)
  • Second Pi-hole instance for redundancy
  • Pi-hole sync method decision (gravity-sync vs Teleporter)
  • Move Traefik VM to Lab VLAN (currently on Private from original bridge setup)
  • Raise TTLs to 3600 once buildout settles

Next Session

Phase 2 — wildcard cert via Traefik + Cloudflare DNS-01. DNS layer is done.