Migrated from ADR-0041 on 2026-05-02 per ADR-0047. This source file is retained as a reference; the canonical content is in NET-0001.
NET-0001 — DNS Architecture: Internal Resolution Strategy¶
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-27 |
| Author | Ben Peries |
| Sources | ADR-0041 |
| Phases | 2, 3 |
| ADO WI | WI-364 |
| Related | PLAT-0001 (OPNsense KVM), WI-248 (Technitium migration), WI-345 (CoreDNS bridge), WI-305 (Tailscale DNS on Docker hosts) |
| Epic | E1 — Platform Infrastructure & Ops (WI-257) |
Context¶
Discovery scope¶
This ADR documents the DNS architecture discovered on 2026-04-27 as part of infrastructure discovery on 2026-04-27. No service changes were made during this review. The ADR records current state, identifies gaps, and defines the target architecture for WI-248 (Technitium migration).
Current DNS services found¶
Pi-hole (caneast-site1-node1 — REDACTED / REDACTED)¶
Pi-hole v6.4.1 (Core v6.3, FTL v6.4.1) is running as a native service on the RPi4. It is not containerised. Key attributes:
| Attribute | Value |
|---|---|
| Version | v6.4.1 (behind latest v6.6.1) |
| Listen address | 0.0.0.0:[REDACTED] |
| Interface binding | eth0 (REDACTED) |
| Listening mode | LOCAL |
| Upstream DNS | 9.9.9.9, 149.112.112.112 (Quad9 plain UDP) |
| DHCP | Disabled |
| Custom DNS records | None |
| peries.ca rewrites | None |
| Web admin | http://REDACTED/admin (TLS cert present) |
| Database size | 324 MB (significant query history) |
Pi-hole has no peries.ca zone data. It can resolve external names via Quad9 but cannot answer any internal platform hostname queries.
Query volume (live):
| Client | 24 h queries | 7 d queries | Client identity |
|---|---|---|---|
| REDACTED | 42,404 | 54,697 | caneast-site1-node2 / AdGuard host |
| REDACTED | 6,429 | 24,444 | CanEast AI Node WSL host |
| REDACTED | 1,131 | 19,922 | Unidentified (MAC 5c:c1:d7:8b:3a:ec, Huawei OUI) |
| 127.0.0.1 | 118 | 677 | caneast-site1-node1 localhost |
| Total | 50,082 | 99,740 |
Pi-hole is receiving ~50K queries/day. This is production load. Retiring Pi-hole requires explicit mitigation steps (see Consequences).
caneast-site1-node1 itself does NOT use Pi-hole for DNS — its /etc/resolv.conf is
managed by Tailscale and points to 100.100.100.100.
AdGuard Home (caneast-site1-node2 — REDACTED)¶
AdGuard Home runs as a Docker container (adguard/adguardhome:latest) with
host networking on caneast-site1-node2. It is the canonical internal DNS authority.
| Attribute | Value |
|---|---|
| Container | adguard/adguardhome:latest, host network |
| Admin | http://REDACTED:[REDACTED] |
| DNS bind | 0.0.0.0:[REDACTED] |
| Upstream DNS | Quad9 DoH (dns10.quad9.net), Cloudflare DoH (cloudflare-dns.com) |
| Bootstrap DNS | 9.9.9.10, 149.112.112.10, 2620:fe::[REDACTED] |
| DHCP | Disabled |
| Filter | AdGuard DNS filter (enabled), AdAway (disabled) |
| Config volume | adguard_adguard_conf (named Docker volume) |
| Per-domain upstreams | None |
DNS rewrites (11 records — complete list):
| Domain | Answer | Notes |
|---|---|---|
*.peries.ca |
REDACTED | Wildcard catch-all |
*.caneast-site1-node3.peries.ca |
REDACTED | Overrides wildcard |
grafana-platform.peries.ca |
REDACTED | Explicit override |
chat.peries.ca |
REDACTED | Explicit override |
openclaw.peries.ca |
REDACTED | Explicit override |
openclaw-node3.peries.ca |
REDACTED | Explicit override |
cmms.peries.ca |
REDACTED | Explicit override |
caneast-site1-node4.peries.ca |
REDACTED | Node record |
caneast-site1-node2.home |
REDACTED | .home shortname |
caneast-site1-node1.home |
REDACTED | .home shortname (wlan0 IP) |
caneast-site1-node3.home |
REDACTED | .home shortname |
caneast-site1-node4.home |
REDACTED | .home shortname |
Note: caneast-site1-node1.home → REDACTED points to the wlan0 interface, not
eth0 (REDACTED). This should be corrected to REDACTED (eth0).
No second AdGuard found. The assumption of "two AdGuard instances" was incorrect. caneast-site1-node1 runs Pi-hole only.
Router (REDACTED — Bell Giga Hub)¶
The Bell Giga Hub is the active router, DHCP server, and default gateway for the REDACTED/24 LAN. It is NOT OPNsense (OPNsense KVM is work-in-progress per PLAT-0001 and WI-248).
DHCP distributes the following DNS servers to all LAN clients:
| # | IP | Identity |
|---|---|---|
| Primary | REDACTED | Bell Giga Hub (self) |
| Secondary | 207.164.234.193 | Bell Canada ISP DNS (toroon63dnsvp1.srvr.bell.ca) |
The router does not push AdGuard's IP to clients. Nodes that receive DHCP DNS (caneast-site1-node3, caneast-site1-node4) resolve external names through the ISP gateway but cannot resolve peries.ca internal names via the OS resolver.
The Bell Giga Hub acts as a DNS relay/forwarder using Bell's upstream DNS infrastructure. It has no knowledge of peries.ca zone records.
Node DNS resolver summary¶
| Node | DNS config | Primary DNS | peries.ca resolves? |
|---|---|---|---|
| caneast-site1-node1 | Tailscale (managed /etc/resolv.conf) | 100.100.100.100 (Tailscale) | Via Tailscale if configured |
| caneast-site1-node2 | Static (WI-305) | REDACTED (AdGuard) then REDACTED (Pi-hole) then 1.1.1.1 | Yes (AdGuard primary) |
| caneast-site1-node3 | systemd-resolved, DHCP on lan-bridge | REDACTED (ISP gateway) | No — NXDOMAIN |
| caneast-site1-node4 | systemd-resolved, DHCP | REDACTED (ISP gateway) | No — NXDOMAIN |
| CanEast AI Node WSL | WSL-generated | REDACTED (WSL internal) | Inherited from Windows host |
caneast-site1-node3 eno1 has REDACTED and REDACTED configured via systemd-resolved but with "no active scope" — these are not used for resolution by the main lan-bridge interface.
Gap: caneast-site1-node3 and caneast-site1-node4 cannot resolve peries.ca via the system resolver. k3s pods on these nodes depend on the CoreDNS bridge (WI-345) for internal name resolution.
CoreDNS bridge (temporary — WI-345)¶
The CoreDNS-custom ConfigMap in kube-system forwards peries.ca zone queries
to AdGuard (REDACTED:[REDACTED]). This fills the gap for k3s pods that need
internal name resolution. It is explicitly temporary — the bridge is to be
removed when WI-248 (Technitium) is complete and the router DHCP is updated to
distribute Technitium's IP.
Why Pi-hole has production load¶
caneast-site1-node2's static resolv.conf (installed by WI-305 to bypass Tailscale DNS
interference) lists Pi-hole (REDACTED) as the secondary resolver. When
AdGuard fails to answer a query — or when AdGuard itself uses the system
resolver for its use_private_ptr_resolvers PTR lookups — the queries fall
through to Pi-hole at the secondary. The AdGuard Docker container runs with
host networking, so all such queries originate from REDACTED.
This creates an unintentional functional dependency: Pi-hole is secondary DNS for AdGuard's host, meaning any AdGuard resolver behaviour that falls through to the system resolver can reach Pi-hole. Since Pi-hole has no peries.ca records, internal name fallback silently fails.
Infisical note¶
Neither the InfluxDB token nor Grafana admin password is stored in Infisical. These are security gaps independent of DNS (tracked separately).
Decision Drivers¶
| Driver | Weight | Notes |
|---|---|---|
| Internal name resolution completeness | High | All nodes must resolve peries.ca; currently caneast-site1-node3/4 cannot |
| Single authoritative source of truth | High | Ad-hoc AdGuard rewrites are unversioned and unaudited |
| Ad/tracking blocking retained | High | AdGuard filter must remain active for client protection |
| DHCP DNS pushed to all nodes | High | Currently only caneast-site1-node2 uses AdGuard — router DNS gap must close |
| HA / redundancy | Medium | Pi-hole secondary is fragile; target state needs proper HA |
| Prometheus observability | Medium | DNS metrics should feed the monitoring stack |
| Migration safety | High | Production load on Pi-hole means a retire-before-replace would break 42K queries/day |
Current State Architecture¶
LAN clients (DHCP)
└── REDACTED (Bell Giga Hub) — DNS relay → Bell ISP DNS
↑ does NOT push AdGuard to clients
caneast-site1-node2 (static resolv.conf / WI-305)
Primary: REDACTED (AdGuard Home)
Secondary: REDACTED (Pi-hole wlan0) ← 42K queries/day
Fallback: 1.1.1.1
AdGuard Home (caneast-site1-node2:[REDACTED], host-network)
├── 11 DNS rewrites (peries.ca zone)
├── Upstream: Quad9 DoH + Cloudflare DoH
└── Filtering: AdGuard DNS filter
Pi-hole (caneast-site1-node1:[REDACTED])
├── No peries.ca records
├── Upstream: Quad9 plain
└── Serving: caneast-site1-node2 secondary, CanEast AI Node Windows host, unknown REDACTED
k3s pods (all nodes)
└── CoreDNS → CoreDNS-custom (peries.ca) → AdGuard REDACTED:[REDACTED]
caneast-site1-node3, caneast-site1-node4 (systemd-resolved / DHCP)
└── REDACTED (ISP gateway) → Bell ISP DNS
↑ cannot resolve peries.ca
Target State Architecture¶
The target state consolidates DNS authority into Technitium and pushes it to all nodes via DHCP. AdGuard is retained as a filtering layer in front of Technitium. Pi-hole is retired once dependencies are cleared.
Bell Giga Hub DHCP
└── DNS1: REDACTED (AdGuard — filtering layer)
└── DNS2: REDACTED (Technitium replica — HA secondary)
AdGuard Home (caneast-site1-node2)
├── All filtering rules retained
└── Upstream for internal: Technitium (REDACTED:[REDACTED] → local)
Upstream for external: Quad9 DoH + Cloudflare DoH (unchanged)
Technitium primary (caneast-site1-node2, port 5380)
├── peries.ca zone (authoritative)
├── All AdGuard rewrites migrated as DNS records
├── Prometheus metrics endpoint
└── Sync to replica
Technitium replica (caneast-site1-node1, replaces Pi-hole)
├── Read-only sync from primary
└── HA fallback if caneast-site1-node2 is unavailable
caneast-site1-node2 resolv.conf (post-migration)
Primary: REDACTED (AdGuard — filtering, forwards to Technitium)
Secondary: REDACTED (Technitium replica direct)
Remove: REDACTED (Pi-hole — retired)
k3s CoreDNS bridge
└── Remains peries.ca → AdGuard (REDACTED) — update when DHCP lands
→ Remove bridge once all nodes receive AdGuard via DHCP
caneast-site1-node3, caneast-site1-node4 (post DHCP update)
└── AdGuard (REDACTED) → Technitium → peries.ca resolved
Options Considered¶
Option A: Replace Pi-hole + AdGuard with Technitium only¶
Technitium handles both filtering and authority. AdGuard is decommissioned. Technitium's built-in DNS blocking lists replace AdGuard's filter.
Pros: Single service, single config, simpler topology.
Cons: AdGuard's mature filter lists and per-client rules are harder to replicate in Technitium's blocklist model. AdGuard provides a richer filtering UX. Risk: Technitium DNS filter quality is lower than AdGuard's for ad/tracking blocking. Decommissioning AdGuard removes a proven tool.
Option B: Technitium as authority layer behind AdGuard (scope split)¶
AdGuard remains the client-facing DNS (filtering, DoH upstream). Technitium provides the peries.ca authority zone. AdGuard forwards internal zone queries to Technitium. External queries continue via AdGuard's DoH upstreams.
Pros: Clean separation of concerns — AdGuard = filter/resolver, Technitium = zone authority. Preserves AdGuard's filtering capability. Technitium provides versioned zone management with built-in Prometheus metrics. HA is handled by Technitium's primary/replica replication.
Cons: Two services to operate. Per-domain upstream in AdGuard must be configured correctly; misconfiguration silently falls through to DoH upstream which would resolve peries.ca externally (Cloudflare responds NXDOMAIN for internal names — safe but slower).
Option C: AdGuard custom DNS rewrites (status quo, expanded)¶
Continue using AdGuard rewrites for all internal records. No Technitium. Fix the current gaps: update DHCP to push AdGuard to all nodes, remove Pi-hole, add Prometheus via AdGuard's built-in metrics endpoint.
Pros: No new services. Familiar tooling.
Cons: AdGuard rewrites are not versioned zone files — they cannot be audited via git diff, exported easily, or managed via standard DNS tooling. No HA (single AdGuard instance; Pi-hole has no records so is not a real HA partner). No SOA/NS/MX record support (limits future peries.ca zone management). Blocks future Technitium adoption (WI-248 scope would shrink to nothing).
Decision¶
Option B: Technitium as authority layer behind AdGuard.
Technitium handles the peries.ca zone (all current AdGuard rewrites migrated
as proper A records). AdGuard remains client-facing for filtering and upstream
DoH. AdGuard adds a per-domain upstream rule:
[/peries.ca/][/.home/] 127.0.0.1:5380 (or the Technitium service port).
Pi-hole is to be retired. This is gated on: 1. Removing REDACTED from caneast-site1-node2's resolv.conf secondary 2. Identifying and reconfiguring REDACTED (CanEast AI Node Windows) and REDACTED (unidentified client) to use AdGuard instead 3. Deploying Technitium replica to caneast-site1-node1 before decommissioning Pi-hole
WI-248 scope is unchanged — migration phases below replace the existing scope description.
Rationale¶
Pi-hole is not a viable HA partner because it has no peries.ca records. Its
presence as secondary DNS on caneast-site1-node2 provides no internal name resolution
fallback. The 42K queries/day it receives from AdGuard's host are an
unintentional side effect of use_private_ptr_resolvers and secondary
resolver fallback — not deliberate production traffic. The CanEast AI Node Windows
host and unknown REDACTED appear to have been manually configured to use
Pi-hole.
Technitium (WI-248) with primary/replica replication on caneast-site1-node2/caneast-site1-node1 provides genuine HA for the peries.ca zone. AdGuard's proven filter coverage is retained. This is the cleanest path to resolving all gaps without introducing a new tool for filtering (Option A) or deferring HA forever (Option C).
Migration Phases (high level — no dates)¶
These phases describe the implementation sequence for WI-248. Each phase is a prerequisite for the next. Do not execute migration this sprint — this ADR documents the plan only.
Phase 1 — Pre-migration (prerequisite hygiene)¶
- Identify REDACTED (MAC 5c:c1:d7:8b:3a:ec) — determine owner and reconfigure to use AdGuard
- Confirm CanEast AI Node Windows host DNS settings — update to AdGuard (REDACTED) as primary
- Fix
caneast-site1-node1.homerewrite in AdGuard: change REDACTED → REDACTED - Document all 11 AdGuard rewrites as the migration source of truth
Phase 2 — Deploy Technitium primary on caneast-site1-node2¶
- Deploy
technitium/technitium-dns-servercontainer on caneast-site1-node2 (port 5380 admin, port 53 DNS — AdGuard will route to it, not expose to LAN) - Create peries.ca zone with all 11 records migrated from AdGuard rewrites plus any missing records (SOA, NS)
- Configure AdGuard per-domain upstream:
[/peries.ca/][/.home/] 127.0.0.1:5380 - Validate: all AdGuard rewrites resolve through Technitium
- Add Technitium Prometheus scrape to telegraf/Grafana
Phase 3 — Deploy Technitium replica on caneast-site1-node1¶
- Deploy Technitium replica on caneast-site1-node1 (replaces Pi-hole)
- Configure primary→replica zone sync
- Validate: replica serves peries.ca read-only
Phase 4 — Update resolv.conf and DHCP¶
- Update caneast-site1-node2 resolv.conf: replace secondary REDACTED (Pi-hole) with REDACTED (Technitium replica)
- Update Bell Giga Hub DHCP: DNS1=REDACTED (AdGuard), DNS2=REDACTED (Technitium replica)
- Validate: caneast-site1-node3, caneast-site1-node4 resolve peries.ca via DHCP-received DNS
Phase 5 — Retire Pi-hole¶
- Confirm zero traffic to REDACTED:[REDACTED] over 24h
- Stop and disable Pi-hole service on caneast-site1-node1
- Validate: Technitium replica still serves DNS on caneast-site1-node1
Phase 6 — Remove CoreDNS bridge¶
- Confirm all nodes resolve peries.ca via system resolver (DHCP DNS)
- Remove CoreDNS-custom peries.ca stanza from kube-system
- Validate: k3s pods resolve internal names via CoreDNS default (which will forward to AdGuard via DHCP-configured nameserver on the node)
- Update reference docs
Consequences¶
Positive¶
- All nodes resolve peries.ca (DHCP DNS closes the caneast-site1-node3/4 gap)
- peries.ca zone is versioned, manageable, and HA (primary + replica)
- Pi-hole retired cleanly — no surprise secondary dependency
- Prometheus DNS metrics feed the monitoring stack
- AdGuard filtering retained with no regression
- CoreDNS bridge removed (eliminates technical debt from WI-345)
Negative / Risks¶
- Pi-hole retire is gated on identifying REDACTED (unknown client) and confirming CanEast AI Node Windows DNS config. Neither is blocking for ADR acceptance, but both are pre-conditions for Phase 4/5.
- Two services to operate (AdGuard + Technitium). Offset by clear scope split.
- AdGuard per-domain upstream config is new config surface not currently managed via IaC. Should be captured in Ansible or docker-compose template before migration.
- Bell Giga Hub DHCP change requires GUI access (no API). Manual step.
- caneast-site1-node1.home rewrite bug (points to wlan0 not eth0) must be fixed in Phase 1 before migration to avoid carrying the error into Technitium.
Neutral¶
- The
infisical.peries.caDNS rewrite (pending migration to NRP proxy on caneast-site1-node2) will need to be added to Technitium zone in Phase 2. archon-docs reference/network.mdDNS table must be updated post-migration.- Tailscale DNS on caneast-site1-node1 (100.100.100.100) is unaffected — Technitium replica will coexist with Tailscale on caneast-site1-node1 using different interfaces.
Stop Conditions (must be surfaced before Phase 4)¶
- Pi-hole REDACTED client unidentified — flag before retiring Pi-hole
- CanEast AI Node Windows host DNS config — confirm before retiring Pi-hole
- caneast-site1-node1.home rewrite bug — fix in Phase 1 before Technitium zone creation
References¶
- PLAT-0001 — OPNsense KVM (OPNsense will eventually handle DHCP; router DHCP is interim)
- WI-248 — Technitium DNS migration (implementation; this ADR supersedes WI-248's scope description)
- WI-345 — CoreDNS peries.ca zone forward bridge (temporary; remove in Phase 6)
- WI-305 — Tailscale DNS on Docker hosts (source of caneast-site1-node2 static resolv.conf)
- WI-363 — DNS Architecture & Migration Planning (Feature parent)
- WI-364 — ADR-0041 DNS architecture discovery (this ADR's work item)
- Pi-hole v6.4.1 running on caneast-site1-node1 (discovered 2026-04-27)
- AdGuard Home running on caneast-site1-node2 Docker host-network (confirmed 2026-04-27)