DNS Stack — AdGuard + Technitium + Mullvad

Tot traficul DNS din casă trece prin două servere locale + unbound + Mullvad encrypted. Aici e ce rulează unde, de ce e împărțit pe două NAS-uri și ce tuning am făcut pe 2026-05-28 ca să servească agresiv din cache.

Ce este

Lanțul de rezoluție DNS pentru toate device-urile din casă + DoH public pentru iPhone-urile roaming. Două servere principale:

  • AdGuard Home — Jarvis (192.168.0.100). Front door LAN. Blocking (ad/tracker filtering), rewrite-uri zone proprii, upstream-uri DoT către Mullvad. UI: http://192.168.0.100:5380 (LAN) și https://dns.sabin.uk (public via CF Tunnel Jarvis).
  • Technitium — Gideon (192.168.0.101). Forwarder secundar + endpoint DoH public (https://dns.andrei.uk/dns-query) pentru telefoanele roaming.

Plus unbound (Jarvis side, în același compose stack) + Mullvad DoT upstream (base.dns.mullvad.net 194.242.2.4:853) + acme.sh container pentru cert renewal auto.

Cum funcționează — diagramă

client local (LAN)
  └─→ AdGuard (Jarvis :53) ─┬─→ unbound :5053 ─→ Mullvad DoT (94.242.2.4:853)
                            └─→ direct DoH la Mullvad (pentru zone proprii)

client local (LAN, secondary)
  └─→ Technitium (Gideon :53) ─→ DoH HTTPS ─→ AdGuard (:5443) ─→ unbound ─→ Mullvad

iPhone roaming (cellular)
  └─→ CF edge ─→ CF Tunnel gideon-dns-stack ─┬─→ path /dns-query ─→ Technitium :8053 (DoH-over-HTTP)
                                              └─→ alte path-uri    ─→ Technitium :5380 (web UI)

iPhone home (WiFi, DHCP)
  └─→ AdGuard :53 (DHCP primary) ─→ ... (vezi sus)

DHCP-ul de pe router împinge .100 primary + .101 secondary. Toate device-urile LAN rezolvă prin AdGuard implicit.

Tuning recent — 2026-05-28 (cache-agresiv)

Operator a observat că Technitium întreba upstream-ul AdGuard pentru aceleași domenii blocked, de mai multe ori pe oră. Defeats the point of having Technitium ca local cache. Două schimbări coordonate:

1. AdGuard blocking_mode: defaultnxdomain. Înainte: domenii blocked întorceau 0.0.0.0 A-record (NOERROR). Astea cădeau sub cacheMinimumRecordTtl în Technitium, care era 10s. Bump-ul minTTL la 24h ar fi prins blocks, dar ar fi blocat și CDN-uri legitime cu TTL natural mic (prost).

După: blocks întorc NXDOMAIN. Cad sub cacheNegativeRecordTtl în Technitium (bumped la 72h). Domeniile legitime își păstrează TTL natural, floor 30min. Best of both worlds.

2. Technitium cache scaling (round 2 evening):

SettingÎnainteDupă
cacheNegativeRecordTtl60s259200 (72h)
cacheMinimumRecordTtl10s1800 (30min)
cacheFailureRecordTtl10s300 (5min)
cacheMaximumRecordTtl7d30d
cachePrefetchTrigger92 (prefetch agresiv)
cacheMaximumEntries50k500000 (10×)
logQueriesFalseTrue

3. AdGuard cache scaling (round 2 evening):

SettingÎnainteDupă
cache_size64 MB512 MB (8×)
cache_ttl_min60s1800 (30min, matches Technitium floor)
cache_ttl_max3600 (1h)604800 (7d)
cache_optimistic_max_age12h168h (7d)
upstreams_cache_enabled ×2falsetrue
upstreams_cache_size ×2016 MB
querylog.interval24h2160h (90d)

Backup-uri salvate înainte de fiecare modificare: AdGuardHome.yaml.bak-cache-tune-20260528 și *.bak-blocking-mode-20260528.

Verificare post-tuning:

dig @192.168.0.101 doubleclick.net   # → NXDOMAIN ✓
dig +short @192.168.0.101 google.com  # → 172.217.23.14 ✓

De ce contează

1. Confidențialitate. Tot DNS-ul casei iese encrypted prin Mullvad DoT, nu prin ISP plaintext. ISP-ul nu vede ce domenii rezolvăm.

2. Blocking la nivel rețea. Ads + trackers blocate ÎNAINTE să ajungă pe device. iPhone, smart TV, console — toate beneficiază fără să instaleze nimic.

3. Resilience cu cache mare. Dacă Jarvis pică, Technitium are 500k entries

  • serveStale 3d. Dacă AdGuard restart-uiește, Technitium continuă din cache. Real-world: queries pentru domenii populare nu mai ies din LAN ore în șir.

4. DoH roaming pentru iPhone. Pe celular, telefonul rezolvă tot prin dns.andrei.uk → Technitium → AdGuard (blocking + filtering aplicat la fel). iOS DoH profile: _raw/it_tools/nas/dns-andrei-uk-doh.mobileconfig.

Cum interacționez cu el

Operator-side, lucruri practice:

  • Adaugă un block manual: AdGuard UI → Filters → Custom rules → ||domain.tld^. Apply instant via UI restart.
  • Whitelist temporar: AdGuard UI → Filters → Custom rules → @@||domain.tld^. Aceeași secvență, prefix @@.
  • Vezi ce queries fac device-urile tale: AdGuard UI → Query Log. Filter pe client IP. 90 zile retention.
  • Vezi cache hits Technitium: https://dns.andrei.uk (auth octet). Dashboard → Cache.
  • Forțează refresh: restart container. ssh jarvis "docker compose -f /volume1/docker/dns-stack/docker-compose.yml restart dnsstack-adguard".

Limite / gotchas

  • Technitium DNSSEC validation MUST stay OFF. Deliberate. AdGuard întoarce răspunsuri sintetice (nesemnate) pentru blocks. Un forwarder validating le-ar respinge → SERVFAIL pe orice domeniu blocked DNSSEC-signed (ex: doubleclick.net). DNSSEC real se face mai sus (unbound → Mullvad).
  • AsusWRT DNS Director MUST stay OFF. Incident 2026-05-23: PREROUTING DNAT-ul DSM prinde și bootstrap-ul AdGuard upstream → loop → SERVFAIL → cloudflared can’t bootstrap → toate tunelele dispar. Use DHCP-DNS push în loc.
  • Cloudflared explicit DNS. Ambele containere cloudflared au dns: [1.1.1.1, 1.0.0.1] în compose. Bootstrap-ul lor (argotunnel.com) NU depinde de chain-ul local. Defense-in-depth după 2026-05-23.
  • Slabă redundanță reală. Technitium forwardează DOAR la .100. Jarvis pică → Technitium fail-uiește și el. Nu e high-availability — e dual-resolver pentru caching, nu pentru disaster recovery.
  • Compose-only management. NU folosi DSM Container Manager GUI pentru niciun stack DNS — recreate fără compose dă drop pe networks.aliases, rupe unbound upstream sau tunnel origin. Doar docker compose CLI.

Unde sunt documentate

  • Memory project_adguard_dns_stack.md (Cowork) — doctrina vie + tuning
  • /volume1/docker/dns-stack/ pe Jarvis — compose + AdGuard yaml + unbound
  • /volume1/docker/technitium/ pe Gideon — Technitium config
  • _raw/it_tools/nas/dns-andrei-uk-doh.mobileconfig — iOS DoH profile
  • _raw/it_tools/nas/jarvis_docker_stack_registry.md — stack Jarvis
  • _infra/config/.credentials/INDEX.md — credențiale (AdGuard, Technitium, Cloudflare API)