DNS — the complete spec¶
The single source of truth for how name resolution works across the homelab: the resolution
model, every authoritative record, the operational runbook, the known gotchas, and the
resilience/migration plan. Complements docs/architecture.md (network model) and
docs/host-port-map.md (ports). Last verified end-to-end: 2026-07-19.
Domain:
fmm.house· Host:avat LAN192.168.86.49, tailnet100.98.53.7/fd7a:115c:a1e0::4001:3507· Registrar/authoritative NS: Porkbun.2026-07-16 — model change. We retired split-horizon in favor of one public answer, reachable two ways. Public DNS now returns
A 192.168.86.49andAAAA <tailnet>for every*.fmm.housename. Resolution no longer depends on AdGuard or the Nest forwarding to it — the answer is authoritative in public DNS. AdGuard's rewrite stays only as a local fallback. Why: the old split-horizon made all*.fmm.houseresolution hinge on AdGuard being up on a rebooting box; a single unclean reboot took it down. See §7 for the reasoning and the rebind-safety test.2026-07-18 — the container-resolution culprit was the Nest, not the host. Bridge containers were getting only the tailnet
AAAAfor*.fmm.house(this doc used to blame systemd-resolved). Direct testing showed the truth: the Nest's DNS-rebinding protection was refusing the privateA(dig @192.168.86.1 … A→status: REFUSED) for names that also carry anAAAA, and systemd-resolved merely relayed that. The 2026-07-16 "rebind-safe" test gave a false green because it used an A-only name (a name with only a privateAisn't refused; one that also has anAAAAis). Rebind protection was disabled on the Nest, so it now serves theAeverywhere — host and containers resolve*.fmm.house → .49unaided, and every per-containerdns:/extra_hostsworkaround was removed (§6.1).2026-07-19 — the
AAAAnow carries both the LAN IPv6 and the native tailnet IPv6. We added av's LAN ULAfd00:6176::49as anAAAA, advertised as an approved Tailscale subnet routefd00:6176::49/128(the IPv6 analog of192.168.86.49/32), alongside av's native TailscaleAAAAfd7a:115c:a1e0::4001:3507. Why both: the LAN ULA +/128route gives LAN and route-accepting tailnet devices a native-v6 path, but subnet routes don't cross a node share and aren't used by tailnet peers that don't accept routes — those clients can only reach av's native address, sofd7amust stay published. Happy Eyeballs picks the reachable one per client (LAN / on-tailnet →fd00:6176; shared-in or route-less →fd7a). AdGuard's rewrite servesAAAA fd00:6176only (its wildcard rewrites return one answer per type; the public zone carries both). Sharing detail: §7 +docs/runbooks/tailscale.md.2026-08-02 — the ULA changed, and the published
AAAArotted silently. The old valuefd6e:88e0:2ffe:ea43::49was a SLAAC address derived from the Nest's auto-generated ULA prefix. The router regenerated that prefix (it now advertisesfde8:5432:a881:1::/64), the address vanished with it, and every*.fmm.houseAAAA— AdGuard's rewrite and the public Porkbun zone — kept pointing at an address av no longer held.The failure mode is worse than "no answer".
ip -6 route getsent traffic for the dead address out the RA default route to be dropped, so connections hung rather than failing fast. Happy Eyeballs sometimes fell back to the workingArecord and sometimes did not, which reads as a randomERR_SSL_PROTOCOL_ERRORon any*.fmm.housename. The stale approved Tailscale/128made it worse: route-accepting tailnet peers had that traffic pulled into av, which had no such address — a second blackhole on the same ghost prefix.Fix and the rule that follows from it: av's ULA is now
fd00:6176::49, pinned as a manual address in NetworkManager (ipv6.addresses,valid_lft forever) rather than inherited from RA. Never publish anAAAAfor a SLAAC-derived address — a router firmware update or factory reset silently invalidates it. If the ULA must change again, all three locations move together: AdGuard rewrites, the Porkbun zone, and the Tailscale/128(§below andtailscale.md).
1. Mental model — one answer, reachable two ways¶
Public DNS is authoritative and identical for everyone — it can't tell where you're asking from, so it hands out three private addresses per name (a LAN IPv4, a LAN IPv6, and av's native Tailscale IPv6) and lets the network use whichever it can route to. The two LAN addresses are reached on-LAN directly or off-LAN via an approved Tailscale subnet route; the native Tailscale address is the fallback for peers those routes don't reach (route-less peers, shared-in users):
*.fmm.house (and the apex) -> A 192.168.86.49 (LAN IPv4)
AAAA fd00:6176::49 (LAN IPv6 / ULA)
AAAA fd7a:115c:a1e0::4001:3507 (native tailnet — shares / route-less peers)
| You are asking from… | you get (same everywhere) | you reach av via… |
|---|---|---|
| A LAN device (v4 or dual) | A .49 + AAAA fd00:6176 + fd7a |
.49 or fd00:6176, direct on the LAN |
| A LAN device, IPv6-only | same | fd00:6176, direct on the LAN |
| On-tailnet, off-LAN, accepts routes | same | .49/fd00:6176 via the …/32 + …/128 routes (or fd7a native) |
| On-tailnet, off-LAN, doesn't accept routes | same | fd7a (native — subnet routes need acceptance) |
| Shared-in user (another tailnet) | same | fd7a only — subnet routes don't cross a node share (§7 / tailscale.md) |
| Off-tailnet / off-LAN | same | neither — all three are private (LAN or tailnet) |
Everything funnels to av, which terminates TLS at Traefik (:443) and routes on. The privacy
model is intact: both published addresses are only reachable on your LAN or your tailnet — the
wider internet resolves the names but can route to nothing, and no ports are forwarded.
The two LAN addresses (
.49,fd00:6176) are reached on-LAN directly or off-LAN via an approved Tailscale subnet route (192.168.86.49/32andfd00:6176::49/128); the native tailnetfd7acovers peers those routes don't reach (route-less peers, shared-in users). This replaced split-horizon: instead of one resolver (AdGuard) handing different answers to different networks, one public record set carries all three, and each client uses the one it can route to.
2. Authoritative records¶
2a. Public zone — Porkbun (api.porkbun.com, creds in stacks/traefik/.env)¶
A *.fmm.house -> 192.168.86.49 # wildcard: every name → av's LAN IPv4
A fmm.house -> 192.168.86.49 # apex (wildcards don't cover the apex)
AAAA *.fmm.house -> fd00:6176::49 # av's LAN IPv6 (ULA) — LAN + /128 route
AAAA *.fmm.house -> fd7a:115c:a1e0::4001:3507 # av's native tailnet IPv6 — shares / route-less peers
AAAA fmm.house -> fd00:6176::49 # apex (both AAAAs)
AAAA fmm.house -> fd7a:115c:a1e0::4001:3507
MX fmm.house -> mail.fmm.house (prio 10) # see §4 (mail)
NS fmm.house -> curitiba/fortaleza/maceio/salvador.porkbun.com
- All are wildcards — every
*.fmm.housename gets the LANAand bothAAAAs (LANfd00:6176 - native tailnet
fd7a), so adding a new service needs zero public-DNS work (§5a). - The
Arecords point at a private (RFC1918) address in a public zone. Cloudflare and Google pass it through fine; the Nest's DNS-rebinding protection refused it (for dualA/AAAAnames) until we disabled that on 2026-07-18 — it now passes too (§7). It does mean192.168.86.49is public; it's a LAN IP, low sensitivity, but note it for the eventual public repo. - There used to be explicit
AAAA ha.fmm.house/AAAA mail.fmm.house(pre-wildcard leftovers). They were deleted — an explicit record suppresses the wildcard for that name, which would have starvedha/mailof the newA. The wildcard now governs both. - Porkbun is also the ACME DNS-01 provider: Traefik proves
*.fmm.houseownership by writing a TXT record via the Porkbun API to issue the wildcard cert. That TXT is transient.
2b. Internal resolver — AdGuard Home (stacks/adguard)¶
Runs as a container on av, published on the host's real IPs (see §5c for why specific IPs):
DNS listeners : 192.168.86.49:53 , 100.98.53.7:53 (tailnet) , [::]:53 (udp+tcp)
Admin UI/API : http://192.168.86.49:2003 (login-gated)
DNS Rewrites (now a redundant LOCAL fallback, not load-bearing — public DNS is authoritative):
*.fmm.house -> 192.168.86.49 # A
*.fmm.house -> fd00:6176::49 # AAAA — the ULA ONLY (public zone also carries fd7a; see note)
fmm.house -> 192.168.86.49 / fd00:6176::49 # A + AAAA
Upstream : https://dns.quad9.net/dns-query (DoH)
tls://dns.quad9.net (DoT)
Bootstrap : 9.9.9.9 , 149.112.112.112 (plain IPs — resolve the DoH/DoT hostname)
Fallback : (none)
Filtering : 2 blocklists · user_rules: (empty) · ratelimit 20
AdGuard's rewrite returns A .49 + AAAA fd00:6176 (it previously answered AAAA as NODATA). It serves
only fd00:6176, not the public zone's second AAAA fd7a — AdGuard wildcard rewrites return a
single answer per type. That's fine: every AdGuard-resolving client here is on the LAN (reaches fd00:6176
directly) or accepts routes; the clients that need fd7a (node shares, route-less peers) resolve
via the public zone, which carries both. Since public DNS now carries these itself, the rewrite is no
longer required for resolution — it only helps in the narrow case where a LAN client can reach
AdGuard but not the public internet (e.g. WAN down, LAN up), where it
answers *.fmm.house locally. Its NODATA-for-AAAA (and MX/TXT shadowing) still applies — see §6.
2c. The router — Nest Wifi Pro (192.168.86.1)¶
Hands out itself (192.168.86.1) as the DNS server via DHCP and forwards to a public upstream
(and should forward to AdGuard for ad-blocking, though it currently doesn't — see below).
No longer load-bearing for resolution. Since 2026-07-16,
*.fmm.househas publicA .49records, so LAN devices resolve.49through the Nest regardless of whether it forwards to AdGuard or straight to public — either path returns.49(the Nest serves the privateAnow that rebind protection is off, §7). The only thing the Nest bypassing AdGuard costs you now is ad-blocking, not service access. To restore ad-blocking: Google Home → Wifi → Advanced networking → DNS → Custom =192.168.86.49. The durable fix for both is still the router migration (§7).
3. Resolution flow (worked)¶
LAN laptop ── im.fmm.house? ──► Nest 192.168.86.1 ──► (public or AdGuard) ──► A 192.168.86.49
│ (same LAN → direct)
▼
Traefik :443 (SNI im.fmm.house) ──► file route ──► av:2002 (immich)
Tailnet phone (off-LAN) ── im.fmm.house? ──► public/MagicDNS ──► A .49 + AAAA fd00:6176(LAN) + fd7a(native)
──► fd00:6176 via /128 route (or fd7a native for shares/route-less) ──► Traefik :443 ──► immich
The host av resolves via systemd-resolved, which forwards *.fmm.house to the Nest and now
returns the A .49 and both AAAAs (fd00:6176 + fd7a). (Until 2026-07-18 the Nest's rebind protection
refused the A, so the host — and every bridge container forwarding to it — saw only the then-tailnet
AAAA; that's fixed, §6.1.) A bridge container uses the A (its v4-only bridge can't route the
v6, so the AAAA fast-fails and it falls back to .49); a dual-stack LAN device can use either.
4. Mail / MX¶
- Public:
MX fmm.house → mail.fmm.house, andmail.fmm.housenow resolves (via the wildcards) toA .49+AAAA fd00:6176/fd7a. A sender delivering to<mailbox>@fmm.housedoes the MX lookup and reaches:25(Postmoogle) on the LAN directly, or off-LAN via the approved Tailscale routes. No public inbound mail (both addresses are private; no port-forward). - Internal (implicit MX): AdGuard's rewrite shadows MX (returns NODATA), so a LAN MTA gets no
MX record and falls back per RFC 5321 §5.1 to the apex A (
fmm.house → 192.168.86.49) →:25. This is why we never needed to hand-craft an internal MX record. - Full mail path + both well-known gotchas:
docs/runbooks/email-to-matrix.md.
5. Operational runbook¶
5a. Add a new service hostname¶
Nothing to do in DNS. The public wildcards (A .49 + AAAA fd00:6176 + fd7a) already cover every name.
Just give Traefik a route (container label or stacks/traefik/dynamic/<svc>.yaml) and the wildcard
TLS cert already matches. Only add an explicit record for a non-av target — and if you do, add
both the A and AAAA for it, because one explicit record suppresses the wildcard for that
name entirely (that's what bit ha/mail, §2a).
5b. Verify DNS end-to-end¶
# Public zone is authoritative — every name should return BOTH the LAN A and the LAN AAAA
dig @1.1.1.1 im.fmm.house A +short # → 192.168.86.49
dig @1.1.1.1 im.fmm.house AAAA +short # → fd00:6176::49 AND fd7a:115c:a1e0::4001:3507
dig @192.168.86.1 im.fmm.house A +short # via the Nest → 192.168.86.49 (rebind protection off; A served)
# AdGuard fallback still answers on every interface it binds
dig @192.168.86.49 im.fmm.house +short # → 192.168.86.49 (rewrite)
dig @100.98.53.7 fmm.house +short # → 192.168.86.49 (tailnet listener)
dig @192.168.86.49 google.com +short # → upstream works (Quad9)
# Tailnet reachability of the LAN A/AAAA depends on the approved subnet routes
tailscale status --json | python3 -c "import json,sys;print(json.load(sys.stdin)['Self']['PrimaryRoutes'])"
# → must contain 192.168.86.49/32 AND fd00:6176::49/128
5c. AdGuard :53 bind — why specific IPs¶
stacks/adguard/compose.yml publishes :53 on 192.168.86.49, 100.98.53.7, and [::] —
not 0.0.0.0. This is deliberate: av runs systemd-resolved on 127.0.0.53:53, and a
0.0.0.0:53 publish would collide with it. Binding the real IPs sidesteps the conflict. Do not
change these to 0.0.0.0.
5d. Reboot-recovery playbook ← learned the hard way, 2026-07-16¶
av has a LUKS root (no auto-boot) and an unclean power-loss reboot leaves Docker's
per-container network state corrupt. The nasty part: containers come back looking "healthy"
(their healthcheck only probes an internal port) while their host networking is dead. Symptoms and
fixes seen this incident:
| Symptom | Root cause | Fix |
|---|---|---|
dig @192.168.86.49 → connection refused, but docker ps says healthy |
Container restored with no network attached — docker port <c> empty, NetworkSettings.Networks empty; AdGuard's own :53 was up inside the container |
docker compose … up -d --force-recreate <svc> (a plain start/up does not rebuild the network) |
App crash-loops on getaddrinfo EAI_AGAIN <db> while its DB container is up |
Project network's embedded DNS (127.0.0.11) left stale; recreating only the app onto it doesn't fix it | docker compose … down && … up -d (rebuilds the network object, not just containers) — did this for immich + peertube + ocis |
Bridge container dies on dial <fd7a:…>:443: network is unreachable |
It resolved a *.fmm.house name to only the tailnet IPv6 — the Nest was refusing the private A (rebind protection), so there was no A to fall back to |
Fixed at the source 2026-07-18 (rebind protection off → the Nest serves the A, containers fall back to .49). The old extra_hosts: …:host-gateway pins are now removed (§6.1) |
Fast triage after any reboot:
# any running container with NO network attached? (the "healthy but dead" trap)
for c in $(docker ps --format '{{.Names}}'); do
n=$(docker inspect "$c" --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}')
m=$(docker inspect "$c" --format '{{.HostConfig.NetworkMode}}')
[ -z "$(echo $n|tr -d ' ')" ] && [ "$m" != host ] && [ "$m" != none ] && echo "⚠ $c has no network"
done
# services that exited ~boot time and didn't come back
docker ps -a --format '{{.Names}}\t{{.Status}}' | grep -iE 'exited.*(255|1) .*(minute|hour) ago'
5e. DNS-over-TLS for Android "Private DNS" (ag.fmm.house)¶
Android's Private DNS speaks DoT on :853 with a valid cert only (no DoH, no plain DNS — that's
why plain ag.fmm.house was rejected). We terminate DoT at Traefik, reusing the *.fmm.house
wildcard cert, so no cert lives inside AdGuard:
- traefik.yaml — a dot entrypoint on :853.
- traefik compose — publishes :853 on 192.168.86.49 + 100.98.53.7 + [::] (specific IPs +
[::], not 0.0.0.0+[::] together — those collide, the v6 bind also grabs v4).
- dynamic/adguard-dot.yaml — a TCP router HostSNI(ag.fmm.house) that TLS-terminates and
forwards the plaintext DNS-over-TCP to adguard:53. AdGuard sees normal DNS-over-TCP.
- ag.fmm.house needs a dedicated A → 192.168.86.49-only record in Porkbun (not the dual-stack
wildcard). Android's Private DNS validator probes every resolved address; the wildcard's tailnet
AAAA is unreachable from a Wi-Fi phone → a persistent "DNS server cannot be accessed" warning
even while DNS works over the A. The explicit A suppresses the wildcard AAAA for this name
(Porkbun honors RFC 4592 — allow ~1 min to propagate). Remote reach is then the LAN A via the
approved 192.168.86.49/32 route (mobile Tailscale uses it automatically).
On the phone: Settings → Network & internet → Private DNS → provider hostname → ag.fmm.house.
After a DNS change, toggle Private DNS off→on (or re-enter the hostname) to force Android to
re-resolve + re-validate — it caches both the resolution and the validation result.
⚠️ Strict Private DNS has no fallback. If
ag.fmm.house:853is unreachable, the phone has no DNS at all. It works only on the LAN or with Tailscale connected (the phone resolvesag.fmm.housevia the underlying network, then reaches:853on.49/tailnet). Off-LAN without Tailscale → turn Private DNS off. (This also means the phone now gets AdGuard's ad-blocking +*.fmm.houserewrites wherever it is on the tailnet.)
Verify: kdig +tls @192.168.86.49 -H ag.fmm.house fmm.house, or a Python TLS-to-:853 + DNS probe.
6. Known gotchas (consolidated)¶
- [RESOLVED 2026-07-18] Bridge containers once got ONLY the tailnet IPv6 for
*.fmm.house. Symptom: a container resolving a*.fmm.houseURL got only the unroutable tailnetAAAA(network is unreachable/ a false "down"), with noAto fall back to. The cause was not systemd-resolved (an earlier version of this note said so — wrong): the Nest's DNS-rebinding protection was refusing the privateAfor names that also carry anAAAA, and the host resolver relayed that refusal. Disabling rebind protection on the Nest made it serve theAagain; host and containers now resolve*.fmm.house → .49unaided (verified end-to-end viagetent/create_connectionfrom a bridge — v6 fast-fails,.49answers in ~90 ms). The per-container workarounds were therefore removed: - Removed
dns: [192.168.86.49]from Uptime Kuma. - Removed
extra_hosts: ["…:host-gateway"]from Postmoogle (matrix.fmm.house) and the app mailers beszel / forgejo / ocis / immich (mail.fmm.house). - The fleet-wide
/etc/docker/daemon.json{ "dns": ["192.168.86.49"] }fix is no longer needed, andlk-jwt(Element Call federation tomatrix.fmm.house) resolves correctly too. - Still the best practice for container-to-container traffic: prefer internal service names
(
http://continuwuity:8008) over the public hostname where possible.If the Nest is ever factory-reset or replaced, rebind protection may come back — re-check with
dig @192.168.86.1 <dual-stack-name>.fmm.house A(aREFUSED/empty answer is the tell) and either disable it again or apply thedaemon.jsondns fix above. - The Nest silently failing over off AdGuard now only costs ad-blocking, not resolution. Because
*.fmm.househas publicA .49, LAN devices resolve correctly whether the Nest forwards to AdGuard or to public. Re-assert custom DNS =192.168.86.49(§2c) to get ad-blocking back, but it is no longer an outage when it drifts. - AdGuard "DNS Rewrites" only do A/AAAA/CNAME and NODATA-shadow every other type (MX, TXT, SRV).
Never rely on AdGuard to serve an MX/TXT for
fmm.house; use the implicit-MX apex-A fallback (§4) or a$dnsrewritefilter rule. Full write-up: memoryadguard-ui-rewrites-shadow-mx. - "Healthy" ≠ "reachable" after an unclean reboot. AdGuard's healthcheck pings only its web UI
(
:8083), so LAN DNS can be dead whiledocker psis green. External monitoring is the only reliable signal — done: Uptime Kuma monitor #19 "AdGuard DNS (fmm.house :53)" now queriesfmm.house Aagainst192.168.86.49:53every 60 s (a real DNS check, unlike the HTTP web-UI monitor #2), and alerts to the Matrix "Homelab Updates" room (all monitors now notify there — seedocs/runbooks/monitoring.md). So a dead resolver both goes red and pages.
7. Resilience & the router-native migration¶
What the public-A change fixed (2026-07-16). The old fragility was that all *.fmm.house
resolution hinged on AdGuard being reachable on a rebooting box — and general LAN DNS hinged on the
Nest forwarding to it. A single unclean reboot took AdGuard's networking down and the Nest silently
failed over, so the whole house lost DNS in a way monitoring couldn't see. Publishing the LAN A in
authoritative public DNS decouples resolution from av entirely: every device resolves
*.fmm.house from public DNS regardless of AdGuard or the Nest. av going down now only means the
services are unreachable (they're on av anyway) — it no longer breaks name resolution or general
LAN internet.
The rebind story (2026-07-16 → 2026-07-18). Putting a private (RFC1918) A in a public zone can
be refused by resolvers with DNS-rebinding protection. The 2026-07-16 test (rbtest49.fmm.house
resolving to .49 via Cloudflare 1.1.1.1, Google 8.8.8.8, and the Nest 192.168.86.1) looked
clean — but it used an A-only name, and that was the flaw: the Nest refuses the private A
only for names that also carry an AAAA (i.e. every real service). Public resolvers
(Cloudflare/Quad9/Google) never refuse it; dig @192.168.86.1 git.fmm.house A returned
status: REFUSED while the AAAA passed. Fix: rebind protection disabled on the Nest
(2026-07-18) — it now serves the A for all names. Re-test with
dig @192.168.86.1 <dual-stack-name> A if the router is ever reset or replaced.
What's left for the migration. With resolution handled, the router migration is now about ad-blocking robustness and cleanliness, not a DNS outage risk — a nice-to-have, not urgent.
Options
| Option | What | Pros | Cons |
|---|---|---|---|
| A. Router-native AdGuard (your lean) | A router/appliance that runs AdGuard Home itself (e.g. GL.iNet Flint, or OPNsense/OpenWrt + AdGuard) replaces the Nest | DNS decoupled from av; survives av reboots; the LAN's default gateway is the resolver so no failover gap |
new hardware; re-create rewrites + upstreams there; migrate the "custom DNS" trick away |
| B. Second AdGuard on an always-on low-power device (Pi/mini-PC), handed out as secondary | Cheap; redundancy without replacing the router | secondary-DNS behavior is non-deterministic on the Nest (it can prefer either); two configs to keep in sync | |
| C. Harden in place | TPM2 LUKS auto-unlock + fix the Nest custom-DNS + real healthcheck/monitor + daemon.json dns |
no new hardware | DNS still lives on av; auto-unlock weakens the LUKS threat model |
Recommendation: now that resolution is decoupled, this is no longer urgent. A is still the
tidiest long-term home for ad-blocking + LAN DNS, but there's no outage pressure. Cheap independent
wins whenever: re-assert the Nest custom DNS (ad-blocking), [done] the Uptime Kuma DNS monitor
on 192.168.86.49:53 (catches the "healthy but dead" AdGuard state, §6.4 — now alerts to the
Matrix "Homelab Updates" room), and give AdGuard a real query-based healthcheck. (The daemon.json
dns fix is no longer needed — the Nest serves the A directly, §6.1.) The KVM already covers the
LUKS-unlock console, so TPM2 auto-unlock stays optional.
Replication checklist for a new router running AdGuard¶
Everything needed to rebuild the resolver elsewhere:
1. Rewrites: *.fmm.house → 192.168.86.49 and fmm.house → 192.168.86.49 (A). (Update the IP
if av's LAN address ever changes.)
2. Upstream: DoH https://dns.quad9.net/dns-query + DoT tls://dns.quad9.net;
bootstrap 9.9.9.9, 149.112.112.112.
3. Blocklists: the 2 currently enabled (export from the current AdGuard UI → Filters).
4. Serve DNS to the LAN as the DHCP-advertised resolver (no more "router forwards to a separate
AdGuard" hop — the router is AdGuard).
5. Keep the tailnet listener reachable (or point tailnet clients at MagicDNS + the public zone).
6. Public zone (Porkbun) is independent of the LAN resolver and already carries the authoritative
A .49 + AAAA fd00:6176/fd7a wildcards — a new router inherits correct resolution for free; it only
adds ad-blocking + the local AdGuard fallback (which should serve A .49 + AAAA fd00:6176 to match).
7. Keep av's subnet routes advertised + approved — 192.168.86.49/32 and
fd00:6176::49/128 — they're what make the public A .49 / AAAA fd00:6176 reachable for
off-LAN tailnet devices.
8. Re-run the §5b verification from a LAN client, a tailnet client, and a container.
8. Cross-references¶
docs/architecture.md— overall network model and layersdocs/host-port-map.md— every published port (:53,:2003AdGuard;:443Traefik; …)docs/runbooks/email-to-matrix.md— mail path + well-known gotchas- Memory:
adguard-ui-rewrites-shadow-mx,email-into-matrix-postmoogle