Files
vps/docs/archive/lan-dns-alternatives.md
windyboyandCursor 343c5db415 feat: add gated Compose deploy and make inventory the host source of truth
Keep sanitized Compose sources in-repo with a confirmation-gated Ansible
playbook, add repo-wide validation, tighten runbook ownership/STOP/review
metadata, and archive stale research docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 17:36:39 +08:00

36 KiB

Home-LAN DNS alternatives for the windy LAN (research, 2026-08)

Status: research only. No configuration was changed. This page evaluates resolvers/splitters that are genuinely better than — or meaningfully different from — the current "AdGuard Home (AGH) + mosdns" setup on dns.windy.lan (.36), for a GFW-constrained China home LAN. Claims are cited to primary sources (official repos, official docs, upstream READMEs); anything not verified is flagged as such.

2026-08-12: facts in this page's scope recap were refreshed by W1N-56 live verification — mosdns on .1 is not idle, it is clash's nameserver/default-nameserver (DIRECT-rule real-IP resolution); the canonical decision record is lan-dns-architecture.md (final verdict aligned, Phase 0 kill-test evidence incl. a measured upstream-blackhole degradation gap).

Scope recap (from lan-overview.md, verified 2026-08-06):

  • Clients get DNS via EdgeRouter DHCP option 6 → AGH 192.168.66.36:53.
  • AGH upstreams: dns.alidns.com + doh.pub DoH (load-balanced), fallback https://adg.chans.xyz/dns-query. DNSSEC disabled (known-bad-signature check failed on the selected path). Rewrites: hass.local / hass.windy.lan.
  • gfw OpenWrt (.1) runs OpenClash fake-ip + TPROXY; dnsmasq → clash DNS 127.0.0.1#7874. mosdns on 127.0.0.1:6052 is clash's nameserver/default-nameserver (DIRECT-rule real-IP resolution: domestic → AGH .36:53, foreign → 223.5.5.5/119.29.29.29); it is not in the LAN client query path.
  • No local authoritative PTR source yet; private reverse DNS is a known gap.

1. TL;DR / recommendation

The current stack is already 80% of the answer. AGH is a strong LAN DNS front-end (filtering, rewrites, per-client upstreams, query log, web UI) and its upstream layer — per-domain upstreams plus a per-domain list loaded from a file (upstream_dns_file) — is exactly the mechanism the official docs recommend for accelerating China CDN domains while keeping everything else on a trusted path. AGH configuration: upstreams.

The genuinely worthwhile changes, in order of value:

  1. Add geo-split inside AGH via upstream_dns_file fed by a converted accelerated-domains.china.conf (felixonmars/dnsmasq-china-list): domestic CDN domains → dns.alidns.com / doh.pub; everything else → the trusted foreign path (currently adg.chans.xyz). This is a documented AGH use case, requires no new daemon, and removes the need for mosdns. This is the top recommendation.
  2. Re-enable real DNSSEC by putting validation behind AGH: AGH's enable_dnssec only sets the DO bit — it does not validate (AGH config: DNSSEC). The two realistic ways are (a) point the foreign/trusted default upstream at a validating resolver (unbound, blocky) and re-test a known-bad-signature domain; or (b) insert a validating resolver (blocky is the lightest) between AGH and the upstreams.
  3. mosdns on .1 is resolved, not idle — it is clash's nameserver/default-nameserver (DIRECT-rule real-IP resolution, verified 2026-08-12), so "delete it" is off the table; its role is documented in lan-dns-architecture.md §1. If a future change moves this role to an AGH-side companion, keep in mind mosdns's cache strips EDNS0 and it performs no DNSSEC validation (mosdns v5 executable plugins).

Top-3 alternatives worth pursuing (see §3 for detail):

Rank Option Why
1 AGH with China-list geo-split (upstream_dns_file) Documented AGH pattern; single box; no new service; keeps filtering/rewrites/UI.
2 Blocky as validating backend behind AGH The only "new software" option that adds real in-process DNSSEC validation + conditional per-domain upstreams + ECS in one static binary (blocky README, config).
3 Unbound as validating recursive resolver (replaces forwarders for the foreign path, or whole path) True validation, full recursion (fail-open by nature), private local-zones; heavier ops than AGH's file-driven split.

Explicitly not recommended as replacements here: smartdns and chinadns-ng (both excellent splitters, but neither validates DNSSEC and both lack AGH's filtering/UI/query-log layer, so they add a daemon without closing the DNSSEC gap); mihomo/sing-box DNS as the primary path (couples DNS to the proxy and is fail-closed; keep for proxy-side concerns only); knot-resolver/dnsdist (overkill for a single-operator home LAN).


2. Requirement matrix

Legend: native/built-in · possible with config/lists · absent/ not applicable. "Geo-split" = route domestic vs foreign names to different upstreams. "Anti-pollution" = a mechanism to avoid/adjudicate poisoned answers (IP-verdict or trusted-upstream routing). "DNSSEC" = performs validation in-process (not just forwards DO).

Candidate Geo-split Anti-pollution DNSSEC (validate) Cache Private names / rewrites Ops simplicity License
AGH (current) ◐ per-domain upstreams + list file ◐ via trusted foreign upstream ○ (DO bit only) ● rewrites, per-client, private-PTR ● Docker + UI GPL-3.0
mosdns v5 ● domain/ip list matchers ◐ forward foreign→trusted ● (strips EDNS0) ● hosts/redirect/reverse_lookup ◐ single binary, YAML, no UI GPL-3.0
smartdns ● nameserver groups + domain lists ● bogus-nxdomain / blacklist-ip / trusted groups ○ (no option in config ref) ● serve-expired ● address / local-domain / lease file ◐ single binary, optional WebUI plugin GPL-3.0
chinadns-ng ● chnlist/gfwlist + tag:none IP-test ● IP verdict via chnroute ipset/nftset ● cache/stale/verdict ◐ hosts / dns-rr-ip ◐ single static binary, config file AGPL-3.0
dnsmasq-china-list ◐ (data only) ◐ (via host resolver) ◐ via host ◐ via host ◐ via host ◐ feed lists WTFPL
unbound ◐ forward-zones / RPZ / views ◐ forward-zones + bogus-nxdomain ● serve-expired ● local-zone / local-data ◐ config daemon, no UI BSD-style (NLnet)
blocky ◐ conditional per-domain + client groups ◐ blocking lists + conditional routing ● prefetch ● customDNS / rewrite / hosts ◐ single binary, YAML, REST (no full web UI) Apache-2.0
Technitium ◐ conditional-forwarder zones / apps ◐ blocked lists + forwarding ● persistent ● zones, stub, split-horizon ● .NET + web console GPL-3.0
sing-box ● DNS rules (geoip/geosite) ● rule-based servers + (proxy) sniffing ● LRU + optimistic ● hosts / local server ◐ single binary, JSON GPLv3-family (metadata "other")
mihomo ● nameserver-policy + fallback-filter ● geoip verdict + geosite ● (cache-algorithm) ● hosts; fake-ip-filter for .lan ◐ single binary, YAML not cleanly verifiable (repo obfuscated)
knot-resolver ◐ policy modules ◐ policy + RPZ ● persistent ◐ hints / local data ◐ systemd, Lua config open source (CZ-NIC)
dnsdist ◐ Lua rules (custom) ◐ custom policies ○ (balancer, not validator) ○ (no cache of its own) ○ power tool GPL (PowerDNS)

Notes:

  • "Geo-split" for AGH/blocky/unbound/Technitium is real but requires feeding a China domain list; chinadns-ng/mihomo additionally offer the IP-verdict path for domains not in any list (query both, adopt CN result only if the answer IP is mainland).
  • mosdns v5's cache plugin ignores request EDNS0 and strips response EDNS0 (cache plugin) — relevant because AGH in front of it relies on the DO bit for DNSSEC-capable upstreams.
  • License for mihomo/sing-box/knot-resolver marked conservative: GitHub metadata is "other"/custom or deliberately obfuscated; see §3 caveats.

3. Per-candidate evaluation

3.1 AdGuard Home — advanced upstream routing / built-ins

What it is: Go DNS proxy + adblock + DHCP, LAN DNS front-end (official).

Capabilities relevant here (all from the official configuration page):

  • Per-domain upstreams dnsmasq-style: [/domain/]upstream, wildcards, # = "default upstreams", empty // = unqualified names (upstreams for domains).
  • List from file upstream_dns_file — the docs explicitly call out China CDN acceleration via dnsmasq lists, with the server=/0-100.com/114.114.114.114[/0-100.com/]114.114.114.114 conversion (loading upstreams from file).
  • Upstream modes: load_balance, parallel, fastest_addr; plus fallback_dns used only when primary upstreams fail (config file: dns).
  • Per-client upstreams (clients.persistent[].upstreams), rewrites (filtering.rewrites, incl. wildcard), local_ptr_upstreams for private PTR, ECS (edns_client_subnet with use_custom coarse prefix), optimistic cache (same page).
  • DNSSEC is DO-bit only: enable_dnssec "defines whether the proxy should set the DO flag in the upstream requests" — validation must happen upstream (same page).
  • DoH/DoT/DoQ/DoH3 serving, bind_hosts/ACL guidance (running securely).

Verdict: Already installed and capable of the geo-split itself. The current setup under-uses it: only a load-balanced CN pair + fallback, no per-domain routing and no validating upstream. This is the cheapest "better" state — see §5.

3.2 mosdns v5 — installed, active as clash nameserver (gateway-side)

What it is: "一个 DNS 转发器" (a DNS forwarder) — plugin-based, sequence-driven (README, GPL-3.0, ~3.7k★).

What it does (verified from the v5 wiki and source tree):

  • Servers: udp_server, tcp_server (TLS→DoT), quic_server, http_server (DoH); upstreams in forward support udp, tcp, tls, https, quic, HTTP/3, concurrent racing (concurrent: n picks the fastest) and socks5 (server plugins, executable plugins).
  • Geo-split: v5 data providers are domain_set / ip_set (text list files) plus qname/resp_ip matchers — verified from the current source tree (plugin/data_provider) — and an ipset/nftset exec plugin to push answer IPs to kernel sets. The old v4-style geosite/geoip .dat plugins are not present in the v5 tree; the v5 wiki's own matcher page currently states there are no matcher plugins to document (matcher page). Plan on chnlist/gfwlist-style text lists, not geosite.dat.
  • Cache: yes, incl. optional lazy cache and disk dump; request EDNS0 is ignored and response EDNS0 stripped by the cache plugin (cache).
  • Private names: hosts (domain-rules style, not OS /etc/hosts syntax), redirect, arbitrary (zone records), reverse_lookup (PTR/HTTP lookup).
  • Ops: single binary + YAML; mosdns service install ships a systemd/launchd helper (v5 overview); Docker image exists. No web UI of its own.

Verdict: capable splitter/forwarder, but adds no DNSSEC and no filtering layer, and its cache interferes with EDNS0/DO handling. As a back-end splitter behind AGH it is a legitimate choice only if DNSSEC stays off. Given AGH can do the same per-domain split natively (3.1), mosdns's marginal value here is concurrent upstream racing and ipset/nftset integration — neither is needed at this LAN's scale. Either wire it up properly or remove it.

3.3 smartdns

What it is: local DNS server that queries multiple upstreams, speed-tests the answer IPs and returns the fastest; DoH/DoT/DoQ/DoH3; GPL-3.0, ~11.2k★ (README).

Capabilities (from the official config reference and FAQ):

  • Multi upstream + "returns the fastest IP", unlike dnsmasq all-servers (README).
  • Domain groups: server ... -group <name> + nameserver /domain/group routing, per-bind port flags (-group, -no-speed-check…), client rules/MAC/IP (config options).
  • Anti-pollution tooling: bogus-nxdomain (return NXDOMAIN for poisoned IPs), blacklist-ip, whitelist-ip, ignore-ip, ipset/nftset export (same page).
  • ECS: global edns-client-subnet and per-server -subnet (same page).
  • Cache: cache-size, serve-expired (RFC-like stale), prefetch-domain, persistent cache file (same page).
  • Private names: address, cname, local-domain, dnsmasq-lease-file (same page).
  • DNSSEC: no validation option appears anywhere in the official config reference or FAQ — its pollution model is blacklist/whitelist + trusted groups + speed selection, not DNSSEC (config options, FAQ). Flagged: verify on the version you deploy before relying on it.

Verdict: the classic China-home "best-IP" resolver; good splitter, no DNSSEC, speed-test model optimizes for latency rather than anti-pollution correctness. Not better than AGH+China-list for this LAN; at most a back-end splitter behind AGH, with the same DNSSEC caveat as mosdns.

3.4 chinadns-ng / chinadns2 / dnsmasq-china-list

chinadns-ng (the requested "china-dns-ng"; actual repo zfl9/chinadns-ng, AGPL-3.0, Zig, ~1.4k★) is the maintained rewrite of shadowsocks/ChinaDNS:

  • Two upstream groups (china / trust) + chnlist.txt / gfwlist.txt domain lists; domains are tagged chn/gfw/none (README).
  • tag:none names are queried on both upstreams and the china answer is adopted only if its A/AAAA is a mainland IP (tested against a chnroute ipset/nftset loaded into the kernel); verdict caching avoids re-testing and leaks (README: 原理/verdict-cache).
  • Cache with stale + pre-refresh + optional persistence; DoT upstream (wolfssl build); hosts + dns-rr-ip local records; nftset add for chn/gfw IPs; no DoH by design and no DNSSEC — the author's stated philosophy is "one job, done well" (README).
  • Resource footprint is tiny: ~140 KB baseline, ~2.4 MB with 73k+ chnlist + 5.7k gfwlist entries (README).

chinadns2 (zfl9/chinadns2) is the older C predecessor; effectively superseded by chinadns-ng for new deployments (README not directly fetched — treat as legacy line).

dnsmasq-china-list (felixonmars, ~6.1k★) is data, not a daemon: accelerated-domains.china.conf, bogus-nxdomain.china.conf, apple.china.conf, google.china.conf, with generators for dnsmasq, unbound, bind, dnscrypt-proxy (README, WTFPL per repo).

Verdict: chinadns-ng is the strongest pure splitter for GFW networks (IP verdict beats pure list-based routing for unknown domains), but it cannot validate DNSSEC and brings no filtering UI. As AGH's backend it duplicates what AGH's per-domain upstreams already do; its IP-test mode requires shipping chnroute ipset/nftset into the host. dnsmasq-china-list is best used as the data feed for the AGH upstream_dns_file recommendation in §5.

3.5 unbound

What it is: validating, recursive, caching resolver from NLnet Labs (docs).

  • Real DNSSEC validation by default (trust anchor, chain of trust); the official home-network guide turns it on explicitly (home resolver guide).
  • Full recursion → does not hard-depend on any upstream or proxy; serve-expired (RFC 8767), aggressive NSEC, DoH/DoT/DoQ serving and TLS upstreams, forward-zone/stub-zone/authority-zone, RPZ filtering, views, ECS module (docs index).
  • Private names: local-zone/local-data for *.windy.lan-style names (unbound.conf(5)).
  • No built-in China split: you assemble it with forward-zones fed by dnsmasq-china-list (make unbound generator) + bogus-nxdomain; no UI, no per-client grouping comparable to AGH.

Verdict: the gold standard for the validation half. Best used as (a) the validating upstream behind AGH for the foreign/trusted path, or (b) a full recursive resolver replacing the forwarders if you accept losing AGH-style filtering/UI on top — keep AGH in front for that. System-package based, heavier to operate than blocky but battle-tested.

3.6 blocky

What it is: Go DNS proxy + ad-blocker, "fast and lightweight", single static binary, stateless, Apache-2.0, ~6.9k★ (README).

  • In-process DNSSEC validation: dnssec.validate with DO bit, RRSIG verification, chain-of-trust, NSEC/NSEC3, custom trust anchors, SERVFAIL on bogus (DNSSEC validation docs).
  • Upstreams: parallel_best (2 random resolvers, fastest answer), strict, random; per-client/per-subnet upstream groups; UDP/TCP/DoT/DoH/DoQ/DoH3; DNS stamps; bootstrap DNS (upstreams).
  • Conditional forwarding + customDNS mapping/rewrite (the AGH-rewrite equivalent), hosts files, per-domain upstream routing (custom DNS / conditional).
  • ECS: ecs.useAsClient / ecs.forward (ECS).
  • Cache with min/max TTL + prefetching; optional Redis cache/state sync between instances; query log to SQLite/Postgres/CSV; Prometheus metrics; REST API (README, config).
  • No full web admin UI (metrics/REST/logs only) — an ops trade-off vs AGH's UI.

Verdict: the most attractive new software option for this LAN as a backend behind AGH: it adds real DNSSEC validation + conditional upstream routing + ECS with a single binary and YAML. It has no China-IP-verdict split built in — feed it the China domain list via conditional.mapping/upstream groups, which is fine at this scale. One caveat: no GUI means AGH stays the human-facing front, so AGH→blocky is strictly additive.

3.7 Technitium DNS Server

What it is: self-hosted authoritative and recursive DNS server, .NET, web console, GPL-3.0, ~9.5k★ (README).

  • DNSSEC validation for recursive resolution, forwarders, and conditional forwarders (RSA/ECDSA/EdDSA, NSEC/NSEC3); can also serve signed zones (README).
  • Conditional forwarder zones + bulk conditional forwarding app; blocked-domain lists with regex support and per-client variants; split-horizon/geolocation via DNS Apps; ECS; QNAME minimization (README).
  • Serving side: DoH/DoT/DoQ/DoH3 server, built-in DHCP, persistent cache, caching with serve-stale/prefetch, clustering, HTTP/SOCKS5 proxy for DNS (e.g. over Tor) (README).
  • Heavier footprint (needs .NET; Docker image available) and a full web console with many features this LAN won't use.

Verdict: capable and genuinely feature-rich (a real AGH alternative in the "everything in one box" sense — filtering, private zones, validation, DHCP), but it's more moving parts than this LAN needs, and its geo-split still requires manual conditional-forwarder lists. Not chosen over the lighter AGH+backend approach.

3.8 sing-box / mihomo built-in DNS as the split resolver (fake-ip)

The "third option": let the proxy engine's DNS own resolution, AGH on top.

sing-box DNS object: multiple server types (local, udp, tcp, tls, https, http3, quic, fakeip, hosts, dhcp, mdns…), rule-based server selection by geoip/geosite, LRU cache + optimistic serving, per-query timeout, client_subnet (ECS), reverse_mapping (sing-box DNS docs).

mihomo (Clash.Meta lineage; docs at wiki.metacubex.one): nameserver-policy (geosite/rule-set/domain keys) routes specific domains to specific resolvers; fallback + fallback-filter (geoip=CN, geosite=gfw, ipcidr, domain) adjudicate pollution — a CN resolver's answer is adopted only if the IP is mainland, otherwise the overseas fallback's answer is used; fake-ip/redir-host enhanced mode, fake-ip-filter with e.g. '*.lan' to keep local names on real-IP; per-DNS-server ECS; cache-algorithm (mihomo DNS config).

Assessment for THIS LAN:

  • The pollution adjudication is strong (geoip-verdict fallback, geosite lists), and mihomo already runs on the gateway — so "clash DNS as splitter" is tempting.
  • But the DNS service is coupled to the proxy: foreign resolution rides the proxy path, so when OpenClash/subscription is down, fake-ip mapping and foreign lookups break (partial fail-open only if direct-nameserver/fallback are carefully set). The LAN requirement says must not hard-depend on the proxy (fail-open).
  • fake-ip adds an indirection layer for anything in front of it (AGH on top resolves client IPs against fake-ip ranges; leaks/loops need careful rules).
  • Neither engine validates DNSSEC (no RRSIG verification).
  • sing-box repo license shows "other" in GitHub metadata (not cleanly verifiable); mihomo's repo currently carries deliberately obfuscated content ("Void Terminal" parody) — treat wiki.metacubex.one as the authoritative docs and expect the GitHub surface to change.

Verdict: keep clash/mihomo DNS exactly where it is (proxy-side, TPROXY/fake-ip), do not make it the LAN resolver of record. If you ever want its IP-verdict quality outside the proxy, chinadns-ng gives the same idea with zero proxy dependency.

3.9 knot-resolver / dnsdist — power-resolver options

knot-resolver (CZ-NIC): minimal caching validating resolver, modular/Lua, full DNSSEC validation, forwarding over TLS, query policies, RPZ, views/ACLs, DNS64, persistent cache, serve-stale, even XDP fast-path (docs). As powerful as unbound but with more configuration surface (Lua); overkill for a one-operator home LAN, though it would do the validating-resolver role well.

dnsdist (PowerDNS): "highly DNS-, DoS- and abuse-aware loadbalancer" — routes traffic to backend servers, Lua/YAML config, runtime console, metrics (overview). It is a balancer, not a validator/cache — it fronts other resolvers. Overkill; only relevant if you wanted a multi-backend DNS LB, which this LAN does not.

3.10 Emerging / also-considered options

  • AdGuard Home + dnsmasq-china-list — covered in §3.1/§5; this is the "emerging best practice" for China CDN splits on AGH and is officially documented.
  • pi-hole — adblock/dashboard equivalent of AGH but no per-domain upstream routing worth choosing it over AGH here (not deeply verified for this write-up; AGH already satisfies the role).
  • dnscrypt-proxy — encrypted forwarder with stamp support; a transport option, not a splitter/validator (not deeply verified for this write-up).
  • coredns — plugin-based; geo-split is DIY via plugins; no DNSSEC validation by default (not deeply verified for this write-up).

Follow-up survey of additional popular solutions not covered above, evaluated against this LAN's constraints (fail-open, keep DNS on .36, DNSSEC goal). None of these change the §4/§5 recommendation.

Encrypted-forwarder micro-tools (AGH downstream options, not replacements):

  • dnscrypt-proxy — the classic OpenWrt encrypted forwarder with China-list support and DNS-stamp routing. No in-process DNSSEC validation and no filtering UI; overlaps with AGH's own DoH upstream layer, so its marginal value here is low.
  • dnsproxy (AdGuardTeam) — lightweight DoH/DoT/DoQ forwarder/server. Functionally a subset of AGH's upstream layer; only useful if forwarding logic is deliberately split out of AGH.
  • Stubby — dnsmasq→stubby→DoT (privacy-community pattern). Pure forwarding, no split/filter/validation; adopting it alone would be a downgrade from AGH.

Managed / cloud DNS (zero-ops, not self-hosted):

  • NextDNS / ControlD / AdGuard DNS / Cloudflare — hosted filtering, logs, per-device policies. This LAN already self-hosts AGH + a private adg.chans.xyz fallback, so a cloud service would be a downgrade in control (data leaves the LAN). Only realistic use: add one as an extra foreign-path upstream inside AGH's upstream_dns_file.

Heavier all-in-one resolvers:

  • PowerDNS Recursor — real DNSSEC validation + Lua policy, authoritative and recursive in one. Capable but overlaps unbound; over-provisioned here.
  • BIND9 — classic authoritative/recursive; can validate DNSSEC and, more interestingly, serve as a local authoritative zone that would close the private-PTR gap. As a LAN resolver it lacks AGH's filtering/UI and is heavier to operate; a small dnsmasq authoritative zone is a lighter way to achieve the PTR goal (still deferred until a local authoritative source exists).
  • hickory-dns / trust-dns (Rust) — emerging recursive resolver, DNSSEC friendly, smaller ecosystem/ops track record than unbound/blocky; not yet worth switching for this LAN.

Popular stack patterns (structure, not new software):

  • Pi-hole + unbound — the most common global self-hosted combo (filtering front-end + validating backend). AGH already occupies the Pi-hole role here (and does more), so the equivalent is AGH + unbound/ blocky — exactly the report's recommendation #2.
  • dnsmasq + china-list + smartdns (classic OpenWrt trio) — routes the China list on the gateway itself. Equivalent to co-locating DNS with the proxy host (.1), which violates the fail-open requirement; not recommended for this LAN.

Verdict: the survey adds no better candidate. dnsproxy/dnscrypt-proxy duplicate AGH's upstream layer, cloud DNS is a control downgrade, and the only genuinely new capability (a local authoritative source for PTR) is better served by a small dnsmasq authoritative zone than by replacing the resolver.


4. Architecture recommendation for this LAN

4.1 Preferred architecture (change is config-only)

clients (DHCP option 6 = .36)
   │  UDP/TCP :53
   ▼
AGH .36  (filtering, rewrites, query log, per-client upstreams)
   │  upstream_dns_file:
   │    [/cn-domain-list/] dns.alidns.com doh.pub      ← CN CDN domains (China list)
   │    default: https://adg.chans.xyz/dns-query …      ← trusted/foreign path
   └→ validating resolver (unbound OR blocky) for the foreign path (optional phase 2)
  • Front = AGH stays the single LAN DNS box (filtering/rewrites/UI/query log are its strong suit and are already operating).
  • Split = AGH per-domain upstreams fed by a converted dnsmasq-china-list; no new daemon. This is the documented AGH pattern (upstreams from file).
  • Validation = add a validating resolver behind AGH for the trusted path (blocky simplest; unbound most battle-tested) and re-run the known-bad-signature check that failed before; then flip enable_dnssec.

4.2 Why not the alternatives as front-ends

  • smartdns / chinadns-ng as the LAN resolver: they are pure splitters — no adblock layer, no query log/UI, no DNSSEC. Replacing AGH with either is a capability downgrade; behind AGH they duplicate AGH's built-in split while adding a daemon and losing validation. Only chinadns-ng's IP-verdict mode is genuinely beyond AGH, and it needs kernel ipset/nftset plumbing.
  • mosdns as the AGH backend: viable splitter, but no validation and its cache strips EDNS0/DO (cache plugin), which fights the DNSSEC goal. It is already idle on the box — configure it deliberately or remove it.
  • mihomo/sing-box DNS as the resolver of record: fail-closed + proxy-coupled
    • no validation. Keep as proxy-side concern (§3.8).
  • knot-resolver / dnsdist / Technitium: capable but over-provisioned; Technitium is the only one that would replace AGH wholesale, and there's no benefit worth the migration here.

4.3 Deployment location

  • Keep DNS on dns.windy.lan (.36). It is already the DHCP-advertised resolver; it is a separate VM from the proxy host; DNS therefore stays independent of OpenClash state (fail-open), which is an explicit requirement.
  • Do not move it to gfw (.1): the gateway is where OpenClash injects TPROXY/fake-ip/DNS-hijack rules; co-locating LAN DNS there couples DNS to the proxy and its restart/update lifecycle.
  • A standalone resolver VM adds nothing: both current VMs already sit on the same PVE hypervisor (lan-overview.md §Positioning facts), so a hypervisor outage takes out either placement equally; a second physical host for HA is out of scope for a home LAN.
  • If you ever run a validating resolver + AGH on .36, verify outbound from .36 to the foreign upstreams is not re-hijacked by OpenClash (loop check already mandated in the AGH review).

4.4 Fail-open, DNSSEC, private names — by candidate

Concern How the recommended stack behaves
Fail-open when proxy/subscription down AGH forwards directly to DoH upstreams; .36's outbound is not forced through the proxy in normal ops (no TUN policy routing on .36dns host facts). With unbound/blocky behind, foreign resolution recurses/validates directly, independent of OpenClash. Avoid mihomo-DNS-as-resolver, which is proxy-coupled.
DNSSEC validation Only unbound, blocky, knot-resolver, Technitium validate in-process. AGH sets DO only; mosdns/smartdns/chinadns-ng/mihomo/sing-box do not. Plan: validate behind AGH, or accept "validating public upstream" (confirm with dig +dnssec/known-bad test).
Private names / rewrites AGH rewrites (already in use for hass.windy.lan) + local_ptr_upstreams once a local PTR source exists. blocky: customDNS mapping/rewrite + hosts. unbound: local-zone. All adequate.
Query log / visibility AGH is the best at this of everything evaluated (14-day anonymized log already configured).

5. What would make the current AGH + mosdns setup genuinely better

Concrete, in increasing effort:

  1. Implement the China-list geo-split in AGH itself (upstream_dns_file + converted accelerated-domains.china.conf, default upstreams = trusted foreign path, fallback_dns kept). Official AGH docs describe exactly this pattern (loading upstreams from file); list source: dnsmasq-china-list. Wire a refresh path (cron/ansible) so the list stays current. Re-test CDN resolution and the DNSSEC known-bad domain after.
  2. Put a validating resolver on the trusted path (unbound or blocky), re-run the known-bad-signature check, then enable AGH DNSSEC. Without this, AGH's enable_dnssec is only a DO-flag — the exact reason it is currently off (AGH DNSSEC semantics, host facts).
  3. Either fully configure mosdns (systemd service, sequence, lists) or remove it. Leaving an idle 127.0.0.1:6052 listener documented as "not the active path" is drift. If kept, plan around no-EDNS0 cache + no validation; if removed, drop the listener and its config to reduce surface.
  4. Close the private-PTR gap: once a local authoritative source exists (e.g. dnsmasq on gw, or a tiny authoritative zone), point AGH local_ptr_upstreams at it as the AGH review recommends (AGH review); don't set it before that source exists (dns host facts).
  5. Optional: ECS for CDN geo-accuracy — AGH edns_client_subnet.use_custom with a coarse fixed prefix (or blocky ecs.forward) if measurements show a benefit; note many CN resolvers ignore ECS (AGH ECS).

If the DNS engineering budget is one afternoon, do #1 + #3. If the goal is "real DNSSEC or nothing", do #1 + #2 + #3. Replacing the stack is only justified if you want to abandon AGH's UI/filtering entirely — nothing evaluated here beats it on that axis for this LAN.


Caveats / not verified

  • Live behavior not tested: all capability claims are from primary docs reviewed 2026-08-12; DNSSEC behavior of dns.alidns.com/doh.pub/the adg.chans.xyz path and mosdns's actual version on .36 need on-box dig +dnssec verification (per adguard-home-health).
  • smartdns DNSSEC: the official config reference lists no DNSSEC option; if a newer version added one, it is not reflected here (config options).
  • mosdns geosite/geoip: v5 source tree (fetched 2026-08-12) contains only domain_set/ip_set data providers; if a geosite.dat plugin exists in a release branch, it is not in main (plugin/data_provider).
  • mihomo: the GitHub repo currently shows deliberately obfuscated metadata (see §3.8); capabilities cited from wiki.metacubex.one. sing-box/knot-resolver/dnsdist license identifiers via GitHub metadata are "other"/custom — treat the specific SPDX ids with caution.
  • chinadns2 README was not retrieved (404 on the raw URL); treated as the legacy predecessor of chinadns-ng and not evaluated in depth.
  • Obsidian/personal notes were not consulted; this is upstream-docs-only.