# 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) + idle mosdns" setup on [`dns.windy.lan`](../hosts/dns.windy.lan.md) (`.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. Scope recap (from [`lan-overview.md`](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`. A `mosdns` binary listens on `127.0.0.1:6052` but is **not** on the active 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](https://adguard-dns.io/kb/adguard-home/configuration/). 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](https://github.com/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](https://adguard-dns.io/kb/adguard-home/configuration/)). The two realistic ways are (a) point the foreign/trusted default upstream at a validating resolver ([unbound](https://unbound.docs.nlnetlabs.nl/en/latest/), [blocky](https://0xerr0r.github.io/blocky/latest/configuration/#dnssec-validation)) and re-test a known-bad-signature domain; or (b) insert a validating resolver (blocky is the lightest) between AGH and the upstreams. 3. **Either actually configure mosdns or delete it.** It is idle on the active path; leaving a half-configured listener is surface area with no benefit. If kept, be aware its cache strips EDNS0 and it performs no DNSSEC validation ([mosdns v5 executable plugins](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)). 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](https://github.com/0xERR0R/blocky), [config](https://0xerr0r.github.io/blocky/latest/configuration/)). | | 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-zone`s; 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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)) — 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](https://adguard-dns.io/kb/adguard-home/overview/)). 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](https://adguard-dns.io/kb/adguard-home/configuration/#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](https://adguard-dns.io/kb/adguard-home/configuration/#upstreams-from-file)). - Upstream modes: `load_balance`, `parallel`, `fastest_addr`; plus `fallback_dns` used only when primary upstreams fail ([config file: dns](https://adguard-dns.io/kb/adguard-home/configuration/)). - 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](https://adguard-dns.io/kb/adguard-home/configuration/)). - **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](https://adguard-dns.io/kb/adguard-home/configuration/)). - DoH/DoT/DoQ/DoH3 serving, `bind_hosts`/ACL guidance ([running securely](https://adguard-dns.io/kb/adguard-home/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 but idle What it is: "一个 DNS 转发器" (a DNS forwarder) — plugin-based, sequence-driven ([README](https://github.com/IrineSistiana/mosdns), 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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/fu-wu-qi-cha-jian.md), [executable plugins](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)). - 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](https://github.com/IrineSistiana/mosdns/tree/main/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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/pi-pei-qi-cha-jian.md)). 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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)). - 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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5.md)); 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](https://github.com/pymumu/smartdns)). Capabilities (from the official config reference and FAQ): - Multi upstream + "returns the fastest IP", unlike dnsmasq all-servers ([README](https://github.com/pymumu/smartdns)). - Domain groups: `server ... -group ` + `nameserver /domain/group` routing, per-`bind` port flags (`-group`, `-no-speed-check`…), client rules/MAC/IP ([config options](https://pymumu.github.io/smartdns/configuration/)). - Anti-pollution tooling: `bogus-nxdomain` (return NXDOMAIN for poisoned IPs), `blacklist-ip`, `whitelist-ip`, `ignore-ip`, `ipset`/`nftset` export ([same page](https://pymumu.github.io/smartdns/configuration/)). - ECS: global `edns-client-subnet` and per-server `-subnet` ([same page](https://pymumu.github.io/smartdns/configuration/)). - Cache: `cache-size`, `serve-expired` (RFC-like stale), `prefetch-domain`, persistent cache file ([same page](https://pymumu.github.io/smartdns/configuration/)). - Private names: `address`, `cname`, `local-domain`, `dnsmasq-lease-file` ([same page](https://pymumu.github.io/smartdns/configuration/)). - **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](https://pymumu.github.io/smartdns/configuration/), [FAQ](https://pymumu.github.io/smartdns/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](https://github.com/zfl9/chinadns-ng)). - `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](https://github.com/zfl9/chinadns-ng)). - 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](https://github.com/zfl9/chinadns-ng)). - Resource footprint is tiny: ~140 KB baseline, ~2.4 MB with 73k+ chnlist + 5.7k gfwlist entries ([README](https://github.com/zfl9/chinadns-ng)). **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](https://github.com/felixonmars/dnsmasq-china-list), 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](https://unbound.docs.nlnetlabs.nl/en/latest/)). - **Real DNSSEC validation by default** (trust anchor, chain of trust); the official home-network guide turns it on explicitly ([home resolver guide](https://unbound.docs.nlnetlabs.nl/en/latest/use-cases/home-resolver.html)). - 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](https://unbound.docs.nlnetlabs.nl/en/latest/)). - Private names: `local-zone`/`local-data` for `*.windy.lan`-style names ([unbound.conf(5)](https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html)). - 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](https://github.com/0xERR0R/blocky)). - **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](https://0xerr0r.github.io/blocky/latest/configuration/#dnssec-validation)). - 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](https://0xerr0r.github.io/blocky/latest/configuration/#upstreams-configuration)). - Conditional forwarding + `customDNS` mapping/rewrite (the AGH-rewrite equivalent), hosts files, per-domain upstream routing ([custom DNS / conditional](https://0xerr0r.github.io/blocky/latest/configuration/#custom-dns)). - ECS: `ecs.useAsClient` / `ecs.forward` ([ECS](https://0xerr0r.github.io/blocky/latest/configuration/#edns-client-subnet-options)). - Cache with min/max TTL + **prefetching**; optional **Redis** cache/state sync between instances; query log to SQLite/Postgres/CSV; Prometheus metrics; REST API ([README](https://github.com/0xERR0R/blocky), [config](https://0xerr0r.github.io/blocky/latest/configuration/)). - 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](https://github.com/TechnitiumSoftware/DnsServer)). - **DNSSEC validation** for recursive resolution, forwarders, and conditional forwarders (RSA/ECDSA/EdDSA, NSEC/NSEC3); can also *serve* signed zones ([README](https://github.com/TechnitiumSoftware/DnsServer)). - 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](https://github.com/TechnitiumSoftware/DnsServer)). - 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](https://github.com/TechnitiumSoftware/DnsServer)). - 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](https://sing-box.sagernet.org/configuration/dns/)). **mihomo** (Clash.Meta lineage; docs at [wiki.metacubex.one](https://wiki.metacubex.one/en/config/dns/)): `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](https://wiki.metacubex.one/en/config/dns/)). 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](https://knot-resolver.readthedocs.io/en/stable/)). 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](https://dnsdist.org/)). 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). ### 3.11 Other popular options (survey supplement, 2026-08-12) 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](https://adguard-dns.io/kb/adguard-home/configuration/#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](https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)), 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](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](adguard-home-official-review-2026-08.md)). ### 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 `.36` — [dns host facts](../hosts/dns.windy.lan.md)). 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](https://adguard-dns.io/kb/adguard-home/configuration/#upstreams-from-file)); list source: [dnsmasq-china-list](https://github.com/felixonmars/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](https://adguard-dns.io/kb/adguard-home/configuration/), [host facts](../hosts/dns.windy.lan.md)). 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](adguard-home-official-review-2026-08.md)); don't set it before that source exists ([dns host facts](../hosts/dns.windy.lan.md)). 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](https://adguard-dns.io/kb/adguard-home/configuration/)). 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](../runbooks/adguard-home-health.md)). - **smartdns DNSSEC**: the official config reference lists no DNSSEC option; if a newer version added one, it is not reflected here ([config options](https://pymumu.github.io/smartdns/configuration/)). - **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](https://github.com/IrineSistiana/mosdns/tree/main/plugin/data_provider)). - **mihomo**: the GitHub repo currently shows deliberately obfuscated metadata (see §3.8); capabilities cited from [wiki.metacubex.one](https://wiki.metacubex.one/en/config/dns/). 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. ## Related docs - [lan-overview.md](lan-overview.md) — full topology (verified 2026-08-06) - [hosts/dns.windy.lan.md](../hosts/dns.windy.lan.md) — AGH host facts - [hosts/gfw.windy.lan.md](../hosts/gfw.windy.lan.md) — OpenClash facts - [adguard-home-official-review-2026-08.md](adguard-home-official-review-2026-08.md) — prior AGH config review - [runbooks/adguard-home-health.md](../runbooks/adguard-home-health.md)