Skip to content

AdGuard Home + Unbound

DNS filtering and recursive resolution on infra-services. Replaces PiHole (LXC 104 blocktopus, 192.168.6.80).

Key Value
Compose services/adguard/compose.yml
Admin UI https://adguard.infra.realemail.app
DNS 192.168.6.17:53 (UDP/TCP)
Upstream Unbound container on compose network
Backup tier 2

Full operator README (import script, upstream UI fields, compose notes): services/adguard/README.md in the repo.

Architecture

Clients → AdGuard Home (:53) → Unbound (:53) → root servers
                └─ DNS rewrites (*.lab.local, *.infra.realemail.app)

AdGuard handles filtering and local rewrites; Unbound recurses so the lab does not depend on a public resolver.

Cutover status (2026-06-19)

Item Status
Stack deployed on infra-services Done
Traefik UI + wildcard *.infra.realemail.app rewrite Done
Inventory rewrites imported (~29 + infra wildcard) Done
UFW allow LAN → :53 on infra-services Done
Tailscale prefer-main fix (Servers VLAN same-L2 DNS) Done (Ansible + systemd)
UDM WAN DNS → 192.168.6.17 Owner — finish cutover
All VLAN DHCP DNS → 192.168.6.17 Owner — in progress
IPv6 DNS on UDM Deferred (AdGuard IPv4 only today)
PiHole parallel soak + LXC 104 decom Pending

See Phase 7 Owner Actions — §8.

Servers VLAN caveat (Tailscale)

infra-services advertises 192.168.6.0/24 on Tailscale. Without a prefer-main ip rule, DNS replies to same-subnet clients (e.g. saltierpoop) leave via tailscale0 instead of eth0 and clients time out.

Managed by Ansible (tailscale_prefer_main_routes) and tailscale-local-subnet-routes.service on the host.

Verify

dig @192.168.6.17 google.com +short
dig @192.168.6.17 infra-services.lab.local +short
dig @192.168.6.17 adguard.infra.realemail.app +short

From a Servers VLAN host, all three must succeed before decommissioning PiHole.