netinfra-02 now advertises exit node routes (0.0.0.0/0, ::/0) in addition to the subnet route, matching netinfra-01's configuration. Both nodes are now fully redundant subnet routers + exit nodes. Subnet route (192.168.0.0/22) is approved and active. Exit node routes (0.0.0.0/0, ::/0) require separate approval in the Tailscale admin console. [#377]
6.7 KiB
Tailscale vs. Managed DNS — Architecture Analysis
Status: RESOLVED. The pfv-netinfra-01/02 pair now runs production Technitium DNS with all
knel.netrecords replicated from tailscale-router via the DNS cluster setup. Both LAN IPs serve authoritative records forknel.netand recurse externally. This document records the original conflict, how it was resolved, and the recommended client configuration.
1. Executive summary
Every host in this build runs the Tailscale client, and Tailscale's MagicDNS
manages /etc/resolv.conf by default (pointing at 100.100.100.100). This
previously conflicted with a managed resolv.conf pointing at the LAN
resolvers. The root cause was that the LAN Technitium instances did not have
the knel.net zone populated — that is now fixed.
The pfv-netinfra-01/02 pair now serves identical, authoritative knel.net
records (replicated from production via dns-cluster-setup/).
Both LAN IPs resolve knel.net device names and recurse externally. The
managed resolv.conf is now safe to deploy.
Recommendation: Deploy the managed resolv.conf (.252/.253) on hosts
where you want tunnel-independent DNS. Leave Tailscale managing DNS on hosts
where MagicDNS device names must resolve without a LAN path (e.g. laptops off
-network). See §5 for details.
2. How name resolution works today (post-cluster-setup)
Probed from sectestbed-sandbox (192.168.3.50) after the DNS cluster was
deployed:
| Query path | External name (github.com) |
knel.net device name (pfv-netinfra-01.knel.net) |
|---|---|---|
Via Tailscale resolver (100.100.100.100) |
resolves | resolves → 100.70.181.72 (Tailscale CGNAT) |
Direct dig @192.168.3.252 (Technitium primary, LAN) |
resolves (recurses) | resolves → 100.70.181.72 |
Direct dig @192.168.3.253 (Technitium secondary, LAN) |
resolves (recurses) | resolves → 100.70.181.72 |
Both LAN resolvers now serve knel.net records identically. The
Technitium zone is no longer stale — it was replicated from production
(tailscale-router) as part of the DNS cluster setup.
What changed
Previously (before the DNS cluster setup), querying the LAN IPs returned
NXDOMAIN for knel.net device names because the Technitium knel.net zone
was empty (SOA serial 2025062313, dated 2025-06-23). After replicating
production config to both netinfra hosts, all 124 zones — including
knel.net with all current device records — are served authoritatively on
both .252 and .253.
3. The DNS server pair
| Host | IP | Role | Services |
|---|---|---|---|
| pfv-netinfra-01 | 192.168.3.252 | Primary | Technitium (authoritative, port 5300) + Pi-hole (recursive, port 53) |
| pfv-netinfra-02 | 192.168.3.253 | Secondary | Technitium (replicated via rsync, port 5300) + Pi-hole (recursive, port 53) |
Zone replication is rsync-based (every 60s via systemd timer) because
Technitium's AXFR uses port 53, which is occupied by Pi-hole on these hosts.
See dns-cluster-setup/README.md for
full details.
4. NTP (fully resolved)
NTP is independent of DNS: provisioning/ConfigFiles/NTP/ntp.conf points
directly at the LAN IPs with no DNS dependency:
server 192.168.3.252 iburst
server 192.168.3.253 iburst
Both servers respond with stratum 2/3. The client config uses restrict
rules (not interface listen) to avoid the loopback-binding bug that
prevented sync. This is safe under both Tailscale-managed and LAN-pinned
resolver configurations.
5. Recommendation
On fixed servers (always on-LAN)
Deploy the managed resolv.conf (provisioning/ConfigFiles/Resolv/):
- Points at
.252/.253with failover knel.netrecords resolve on both servers- External names recurse on both servers
- DNS survives
tailscaledoutages (unlike Tailscale-managed DNS)
To prevent Tailscale from overwriting the managed file:
tailscale up --accept-dns=false
On laptops / roaming hosts
Let Tailscale manage DNS (default accept-dns=true):
- MagicDNS resolves
knel.netdevice names via the tunnel - No dependency on LAN reachability
- Accept the
tailscaleddependency (if the tunnel is down, you're off-network anyway)
6. Subnet router HA (redundant routing)
Redmine: #377
Both DNS nodes advertise the LAN subnet as Tailscale subnet routes AND serve as exit nodes, providing full HA for remote monitoring (Uptime Kuma on the Cloudron VPS), off-site access, and internet egress:
| Node | Tailscale IP | Advertised routes | Role |
|---|---|---|---|
| pfv-netinfra-01 | 100.70.181.72 | 192.168.0.0/22, 0.0.0.0/0, ::/0 | Primary subnet router + exit node |
| pfv-netinfra-02 | 100.71.171.20 | 192.168.0.0/22, 0.0.0.0/0, ::/0 | Secondary subnet router + exit node (failover) |
To configure on a node (both flags together):
sudo tailscale set --advertise-routes=192.168.0.0/22 --advertise-exit-node
All routes must be approved in the Tailscale admin console
(https://login.tailscale.com/admin/machines) for failover to activate.
This includes the subnet route (192.168.0.0/22) AND the exit node routes
(0.0.0.0/0, ::/0) — they appear as separate approvals on each machine.
Tailscale uses the primary by default and fails over to the secondary if
the primary's tunnel drops.
Known instability factors (2026-08-06, [#377])
- DERP relay bouncing: netinfra-01 was observed switching between DERP relays (#9, #12, #13) every ~15 minutes, causing brief tunnel disruptions.
- Docker LinkChange events: container create/destroy adds/removes bridge
interfaces, triggering
LinkChange: major, rebindingin tailscaled and momentarily dropping all connections. - resolv.conf immutable flag: netinfra-01 had the
+i(immutable) attribute on/etc/resolv.conf, preventing tailscaled from updating DNS config after link changes. Fixed bychattr -i /etc/resolv.conf.
7. Known items / future work
- Pi-hole upstream configuration. Pi-hole on both hosts should forward
to the local Technitium instance (port 5300) for
knel.netand to an external resolver for everything else. Verify this is configured on both nodes. - Zone transfer via AXFR. Currently using rsync because Technitium's AXFR expects port 53. If Technitium's listen port can be changed, or Pi-hole can be configured to proxy AXFR, the rsync timer could be replaced with native DNS zone transfer.
accept-dns=falseautomation. The provisioning code should set--accept-dns=falseon Tailscale during setup (after deploying the managedresolv.conf) so Tailscale doesn't overwrite it on reboot.