Tutorial completo para subir um servidor DNS na rede doméstica usando AdGuard Home como sinkhole (bloqueio de ads/trackers) e como resolver de DNS interno (gitea.lan.example apontando pra um IP da LAN), opcionalmente combinado com Unbound para resolução recursiva sem depender de Cloudflare/Google/ISP. Também cobre integração com WireGuard, alta disponibilidade com duas instâncias e troubleshooting.
Audiência: desenvolvedor sênior, ambiente de laboratório, Debian/Docker.
1. Visão geral
1.1. DNS sinkhole vs DNS recursivo
DNS sinkhole e DNS recursivo são camadas complementares, não concorrentes.
- DNS sinkhole (AdGuard Home, Pi-hole, Blocky): intercepta queries DNS e responde
NXDOMAINou0.0.0.0para domínios em blocklists (ads, trackers, malware). Para domínios permitidos, encaminha a query para um upstream (Cloudflare, Quad9, Google, Unbound). Trabalha no nível da query, não conhece o conteúdo HTTP. - DNS recursivo (Unbound, BIND, Knot Resolver, Technitium): resolve nomes do zero, começando nos root servers, descendo pra TLD authoritative, depois pro authoritative do domínio. Não depende de um “upstream resolver”, o que melhora privacidade (a query vai direto pro authoritative, não passa por terceiros) ao custo de mais latência no cold cache.
A arquitetura recomendada combina os dois: AdGuard Home como sinkhole na frente, Unbound como upstream recursivo local em 127.0.0.1:5335.
1.2. Comparação: AdGuard Home vs Pi-hole vs Blocky vs Technitium
| Critério | AdGuard Home | Pi-hole | Blocky | Technitium |
|---|---|---|---|---|
| Linguagem | Go (binário único) | PHP + dnsmasq/FTL | Go | C# (.NET) |
| UI admin | Moderna, SPA | Funcional, antiga | Sem UI nativa (Prometheus/Grafana) | Completa, gerenciamento avançado |
| DoT / DoH / DoQ nativo | Sim (server e client) | Não nativo (precisa cloudflared/stubby) | Sim (client) | Sim |
| DHCP server | Sim | Sim | Não | Sim |
| Memória ociosa | ~50-80 MB | ~80-120 MB (FTL+lighttpd+php) | ~30-50 MB | ~120-180 MB |
| Blocklists curadas | Lista enorme pré-curada | Adlists manuais | Manual | Manual |
| Per-client config | Sim, granular | Sim (CLI/admin) | Via grupos YAML | Sim |
| Logs de query | Sim, com filtros ricos | Sim | Stdout/Prometheus | Sim |
| Authoritative DNS | Não | Não | Não | Sim (zonas próprias) |
| DNS recursivo embutido | Não | Não | Não | Sim |
| Curva de aprendizado | Baixa | Baixa | Média (YAML) | Média-alta |
Para um cenário típico de laboratório (dev sênior, quer UI boa e DoT/DoH out-of-the-box, sem ficar tunando dnsmasq), AdGuard Home é o melhor balanço. Pi-hole tem comunidade maior mas mostra a idade. Blocky é ótimo se você vive em YAML/Grafana, mas a falta de UI atrapalha o dia-a-dia. Technitium é o mais completo (faz authoritative e recursivo no mesmo binário), mas é overkill para sinkhole + split-horizon.
1.3. Por que combinar com Unbound
Quando o AdGuard usa Cloudflare/Quad9 como upstream, todo histórico de DNS da casa passa por eles. Mesmo com DoT/DoH (sem ISP vendo no meio), a Cloudflare vê tudo. Unbound local elimina esse intermediário: ele consulta direto os root servers (IANA) e desce pela hierarquia. Vantagens:
- Privacidade: nenhuma única empresa vê todas as queries.
- DNSSEC validation local.
- Cache local muito agressivo.
- Independência de outages de provedor (Cloudflare/Quad9).
Custo: primeira query a um TLD novo é mais lenta (3-4 hops), mas cache resolve isso em segundos.
2. Arquitetura
flowchart TD
subgraph LAN["LAN 192.168.1.0/24"]
L1[Laptop]
L2[Smart TV]
L3[IoT / Câmeras]
L4[Celulares]
end
subgraph VPN["WireGuard 10.0.0.0/24"]
V1[Cliente VPN]
end
subgraph Host["Host do laboratório"]
AGH[AdGuard Home<br/>:53 UDP/TCP<br/>:853 DoT/DoQ<br/>:443 DoH]
UB[Unbound recursivo<br/>127.0.0.1:5335]
end
subgraph Internet["Internet"]
ROOT[Root servers<br/>a-m.root-servers.net]
TLD[TLD authoritative<br/>.com .org .lan?]
AUTH[Domain authoritative<br/>ex: ns1.github.com]
end
L1 & L2 & L3 & L4 -->|query :53| AGH
V1 -->|query via wg0| AGH
AGH -->|domínio bloqueado| BLOCK[NXDOMAIN / 0.0.0.0]
AGH -->|domínio permitido| UB
AGH -->|DNS rewrite lan.example| LOCAL[IP local 192.168.1.x]
UB -->|recursão| ROOT
UB -->|delegação| TLD
UB -->|query final| AUTH
AUTH -.->|resposta| UB
UB -.->|resposta+cache| AGH
AGH -.->|resposta| L1Fluxo de uma query bloqueada: cliente pergunta doubleclick.net -> AdGuard verifica blocklists -> match -> retorna NXDOMAIN ou 0.0.0.0 direto pro cliente. Sem upstream.
Fluxo de uma query permitida: cliente pergunta github.com -> AdGuard verifica blocklists (não bate) -> consulta cache -> miss -> encaminha pro Unbound em 127.0.0.1:5335 -> Unbound consulta root servers -> TLD .com -> ns1.github.com -> retorna IP -> AdGuard cacheia e devolve.
Fluxo de DNS rewrite (split-horizon): cliente pergunta gitea.lan.example -> AdGuard verifica DNS rewrites -> match -> retorna 192.168.1.50 direto, sem ir pro upstream (que rejeitaria o TLD interno).
3. Decisão de hosting
DNS é critical path. Se o servidor DNS cai, a casa inteira reclama “internet não funciona” (porque navegador, push notifications, smart TV, tudo depende). Trate como infra crítica.
| Opção | Prós | Contras | Recomendação |
|---|---|---|---|
| Raspberry Pi 4 dedicado | Hardware barato, low power (~5W), isolado | Cartão SD morre, sem redundância | Boa para secundária |
| Mini PC / NUC dedicado | SSD durável, mais CPU, low power | Investimento inicial | Excelente para primária |
| VM no Proxmox | Snapshot, backup fácil, isolado | Depende do Proxmox estar up | Boa se Proxmox tem HA |
| Container Docker no servidor principal do laboratório | Sem hardware extra, fácil de subir | Compartilha host, reboot do host derruba DNS | Aceitável se host é estável |
| Roteador OpenWRT com AdGuard | Sem hardware extra, integrado | Pouca RAM, sem logs históricos | Backup only |
Recomendação geral: container Docker no servidor principal como primária, e Raspberry Pi 4 com instalação bare-metal como secundária. DHCP do roteador entrega os dois IPs como DNS — clientes failover automaticamente.
Quem quer ir além: VIP com keepalived (VRRP) e config sincronizada via rsync ou git. Mas duas instâncias com DHCP entregando ambas resolve 95% dos cenários sem a complexidade extra.
4. Pré-requisitos
- IP estático na LAN para o host do AdGuard (reserva DHCP no roteador serve).
- Porta 53 UDP+TCP livre no host. Em Debian/Ubuntu modernos,
systemd-resolvedocupa 53 em127.0.0.53:
Se aparecerss -lnup | grep :53 ss -lntp | grep :53systemd-resolved, desabilite ou reconfigure:# Opção 1: desabilitar completamente sudo systemctl disable --now systemd-resolved sudo rm /etc/resolv.conf echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf # Opção 2: manter mas tirar do 53 (edita /etc/systemd/resolved.conf) # DNSStubListener=no sudo systemctl restart systemd-resolved - Portas 80 e 443 livres se for usar DoH (recomendado para clientes externos / VPN).
- Porta 853 livre se for usar DoT/DoQ.
- Acesso root/sudo.
- Firewall liberado: UDP 53, TCP 53, TCP 80, TCP 443, UDP 443 (DoH3), TCP/UDP 853 (DoT/DoQ).
5. Instalação via Docker Compose
Crie a estrutura:
sudo mkdir -p /opt/adguardhome/work /opt/adguardhome/conf
cd /opt/adguardhomedocker-compose.yml:
services:
adguardhome:
image: adguard/adguardhome:latest
container_name: adguardhome
restart: unless-stopped
# Use network_mode host se quiser que o AdGuard veja o IP real do cliente LAN
# (importante para per-client stats e rate limit por IP)
network_mode: host
cap_add:
- NET_ADMIN
volumes:
- /opt/adguardhome/work:/opt/adguardhome/work
- /opt/adguardhome/conf:/opt/adguardhome/conf
# Em bridge mode, descomente network_mode acima e use ports abaixo:
# ports:
# - "53:53/udp"
# - "53:53/tcp"
# - "67:67/udp" # DHCP server (opcional)
# - "68:68/udp" # DHCP server (opcional)
# - "80:80/tcp" # admin + DoH HTTP
# - "443:443/tcp" # admin + DoH HTTPS
# - "443:443/udp" # DoH3
# - "853:853/tcp" # DoT
# - "853:853/udp" # DoQ
# - "3000:3000/tcp" # setup wizard inicial
# - "5443:5443/tcp" # DNSCrypt
# - "5443:5443/udp"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:80/control/status"]
interval: 30s
timeout: 5s
retries: 3Subir:
docker compose up -d
docker compose logs -f adguardhomeNota sobre network_mode: host: mais simples, e essencial se você for usar AdGuard como servidor DHCP. Em modo bridge, todas as queries chegam com IP de origem do gateway do Docker, o que estraga per-client stats.
6. Instalação bare-metal Debian (Raspberry Pi ou VM)
Alternativa ao Docker. Útil pra secundária no Raspberry.
# Garantir porta 53 livre (ver passo 4)
sudo systemctl disable --now systemd-resolved
# Instalação automática (cria systemd service)
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -vO script:
- Baixa o binário pra arquitetura correta (
arm64,amd64, etc). - Instala em
/opt/AdGuardHome/. - Cria systemd service
AdGuardHome.service. - Inicia o serviço.
Verificar:
sudo systemctl status AdGuardHome
sudo ss -lnup | grep :53
sudo ss -lntp | grep :3000Arquivo de configuração: /opt/AdGuardHome/AdGuardHome.yaml (criado após o setup wizard).
Para desinstalar mais tarde: sudo /opt/AdGuardHome/AdGuardHome -s uninstall.
7. Setup wizard
Abra no navegador http://IP-DO-HOST:3000.
Passos:
- Welcome — escolher idioma, next.
- Admin Web Interface — interface e porta para o painel admin. Deixe em
All interfacese porta3000(ou mude para80se quiser administrar via porta padrão; isso libera 3000 e usa 80 para admin + DoH, o que confunde — manter 3000 é mais limpo). - DNS server — interface e porta para queries DNS.
All interfaces+ porta53. - Authentication — criar usuário admin e senha (use uma forte; vai pro
AdGuardHome.yamlem bcrypt). - Configure devices — instruções pra apontar clientes; pode ignorar.
- Finish.
Reentre no painel em http://IP-DO-HOST:3000 (ou na porta que você escolheu) com o usuário criado.
8. Configuração básica
8.1. General Settings
Settings -> General settings:
- Filters update interval: 24 horas (ou menor se você usa lists muito voláteis).
- Use AdGuard browsing security web service: ON (anti-phishing).
- Use AdGuard parental control web service: ON se houver crianças, senão OFF.
- Enforce safe search: ON para Google/Bing/YouTube se quiser SafeSearch forçado.
- Block services: lista grande de toggles (Facebook, TikTok, etc) — útil para ambientes específicos.
- Logs configuration: retenção de query log (24h, 7d, 30d, 90d). 7 dias é um bom default; 90 dias gera GB.
- Statistics configuration: retenção dos stats agregados. 30 dias.
8.2. DNS Settings
Settings -> DNS settings:
Upstream DNS servers: comece com um par bom de DoT (depois trocaremos por Unbound no passo 11):
tls://dns.quad9.net tls://1.1.1.1Modos:
- Load-balancing (default): escolhe servidor menos sobrecarregado.
- Parallel requests: dispara para todos e usa o mais rápido. Mais tráfego, menor latência.
- Fastest IP address: testa qual IP do mesmo nome resolve mais rápido.
Bootstrap DNS servers: resolve os hostnames dos upstreams (
dns.quad9.net, etc). Use IPs puros:9.9.9.10 1.1.1.1Fallback DNS servers: usado quando o upstream principal falha.
8.8.8.8,8.8.4.4. Opcional mas recomendado.Rate limit: 20 q/s por IP é o default. Para LAN doméstica, 50-100 é mais saudável (smart TVs e Apple TV disparam burst legítimo). Em
AdGuardHome.yaml:dns: ratelimit: 50 ratelimit_subnet_len_ipv4: 24 ratelimit_subnet_len_ipv6: 56EDNS Client Subnet: DESABILITADO para privacidade. ECS envia parte do seu IP pro upstream pra CDN escolher edge mais próxima — bom pra latência, ruim pra privacidade.
DNSSEC: marcar “Enable DNSSEC” — AdGuard valida e seta flag
ADquando upstream retorna assinado.Disable IPv6 resolution: marque se sua rede não tem IPv6 funcional (evita timeouts em queries AAAA).
Blocked response TTL: 10s. Tempo que cliente cacheia a resposta bloqueada. Curto para mudanças rápidas em allowlist.
Custom blocking IP: pode deixar em branco (NXDOMAIN, default) ou setar
0.0.0.0para retornar IP nulo. NXDOMAIN é mais limpo.
Salve e teste:
dig @192.168.1.10 github.com
dig @192.168.1.10 doubleclick.net # deve retornar NXDOMAIN9. Listas de bloqueio
Filters -> DNS blocklists -> Add blocklist -> Choose from the list ou Add a custom list.
Recomendação curada (não combine tudo, escolha 2-3 ou vai gerar muito falso positivo):
| Lista | URL | Foco |
|---|---|---|
| AdGuard DNS filter | nativa | Default geral, bem balanceado |
| OISD Big | https://big.oisd.nl/ | Anti-ads + trackers, baixo falso positivo |
| Hagezi Pro | https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.txt | Pro: ads + trackers + malware |
| Hagezi Normal | https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/multi.txt | Mais agressiva |
| Steven Black hosts | https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | Clássica, ads+malware |
Estratégia recomendada: AdGuard DNS filter (nativa) + OISD Big + Hagezi Pro. Cobre ~99% dos ads/trackers sem quebrar serviços legítimos.
Filters -> DNS allowlists: para domínios que sempre devem passar (mesmo se aparecerem em blocklist). Exemplos:
@@||analytics.empresa-cliente.com^Filters -> Custom filtering rules: regras manuais usando sintaxe AdBlock estendida:
! Comentário
||tracker.exemplo.com^
@@||analytics.cliente.com^
||*.doubleclick.net^$important
||google-analytics.com^$client='192.168.1.100'$important força aplicação mesmo contra allowlist. $client= aplica só pra IP/cliente específico.
Filters -> Blocked services: toggles prontos por serviço (Facebook, TikTok, Reddit, etc). Bom pra ambientes com restrição parental.
Auto-update: Settings -> General -> Filters update interval (24h padrão).
10. Resolução de DNS interno (split-horizon)
Você quer gitea.lan.example resolver para 192.168.1.50 quando consultado de dentro da LAN, sem vazar a query pra upstream público (que não conhece o TLD interno).
Há duas formas no AdGuard:
10.1. DNS rewrites (preferida, mais limpa)
Filters -> DNS rewrites -> Add DNS rewrite:
| Domain | Answer |
|---|---|
gitea.lan.example | 192.168.1.50 |
proxmox.lan.example | 192.168.1.10 |
*.lan.example | 192.168.1.10 (catch-all opcional) |
Wildcard funciona. Para CNAME, coloque o hostname destino em vez de IP.
No YAML:
filtering:
rewrites:
- domain: gitea.lan.example
answer: 192.168.1.50
- domain: '*.lan.example'
answer: 192.168.1.1010.2. Custom filtering rules com $dnsrewrite
Filters -> Custom filtering rules:
||gitea.lan.example^$dnsrewrite=192.168.1.50
||proxmox.lan.example^$dnsrewrite=192.168.1.10Mesma coisa do método 1, mas as regras ficam misturadas com bloqueios. Prefira o painel de DNS rewrites para visibilidade.
10.3. Conditional forwarding (encaminhar zona para outro DNS)
Se você roda um DNS authoritative interno (Technitium, BIND, ou o próprio dnsmasq do roteador), pode delegar a zona inteira:
Settings -> DNS settings -> Upstream DNS servers:
[/lan.example/]192.168.1.1
[/lan/]192.168.1.1
[/in-addr.arpa/]192.168.1.1
1.1.1.1
1.0.0.1Sintaxe [/zone/]upstream (dnsmasq-style). Queries para *.lan.example vão pro 192.168.1.1 (roteador), o resto vai pro Cloudflare.
Útil quando o DHCP do roteador registra hostnames automaticamente — em vez de cadastrar cada host à mão no AdGuard, deixa o roteador resolver.
10.4. Cliente identification
Settings -> Client settings -> Add client:
- Identifier: IP, CIDR, MAC, ou ClientID (para DoH/DoT autenticado).
- Name: “Laptop Pessoal”, “Smart TV Sala”, “IoT”.
- Upstreams: pode dar upstream diferente por cliente.
- Tags: per-client blocklists, safe search, etc.
Permite ver no log “Smart TV Sala bloqueou X queries” em vez de IPs anônimos. Também permite rate limit e logging diferenciado.
11. Unbound como upstream recursivo
Instale no mesmo host (Debian/Ubuntu):
sudo apt update
sudo apt install -y unboundCrie /etc/unbound/unbound.conf.d/adguard.conf:
server:
# Interface de escuta - só localhost
interface: 127.0.0.1
port: 5335
# IPv4 only (se sua rede não tem IPv6 funcional)
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
prefer-ip6: no
# Root hints (pacote dns-root-data fornece)
# root-hints: "/var/lib/unbound/root.hints"
# Trust anchor para DNSSEC (auto-managed pelo pacote)
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Segurança / hardening
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: yes
use-caps-for-id: no
# Tamanho de buffer EDNS - 1232 evita fragmentação IP em links
# com MTU típico de 1500 (https://dnsflagday.net/2020/)
edns-buffer-size: 1232
# TTL mínimo e máximo
cache-min-ttl: 300
cache-max-ttl: 86400
cache-max-negative-ttl: 3600
# Privacidade
qname-minimisation: yes
aggressive-nsec: yes
hide-identity: yes
hide-version: yes
# Performance
num-threads: 1
msg-cache-size: 64m
rrset-cache-size: 128m
so-rcvbuf: 1m
# Logging mínimo
verbosity: 0
# Controle de acesso
access-control: 127.0.0.0/8 allow
access-control: 0.0.0.0/0 refuse
# Reduzir DNS leak: não responder a queries de domínios privados
# vindas da internet
private-domain: "lan.example"
local-zone: "10.in-addr.arpa." nodefault
local-zone: "168.192.in-addr.arpa." nodefaultHabilitar e iniciar:
sudo systemctl enable --now unbound
sudo systemctl restart unbound
# Validar
dig @127.0.0.1 -p 5335 github.com
dig @127.0.0.1 -p 5335 dnssec.works +dnssec # deve ter flag "ad"
dig @127.0.0.1 -p 5335 fail01.dnssec.works # deve retornar SERVFAILNo AdGuard, Settings -> DNS settings -> Upstream DNS servers, troque tudo por:
127.0.0.1:5335Bootstrap pode ficar com IPs públicos (9.9.9.10, 1.1.1.1) — só é usado se algum upstream com hostname precisar resolver, o que não é o caso aqui.
Pronto: do AdGuard pra dentro, nenhuma query DNS sai pra Cloudflare/Quad9. Vai direto do Unbound pros root servers.
12. DoH e DoT para clientes externos
Útil para:
- Cliente Firefox/iOS usando DoH apontando para seu AdGuard.
- Cliente Android com “Private DNS” (DoT only).
- Clientes em redes hostis (cafés, hotéis) usando seu AdGuard remotamente.
12.1. Certificado TLS
Você precisa de um certificado válido para o nome (ex: adguard.example.com). Use acme.sh ou certbot com DNS-01 challenge (se o domínio é externo) ou copie um cert existente do nginx.
Exemplo acme.sh com Cloudflare DNS:
acme.sh --issue --dns dns_cf -d adguard.example.com
acme.sh --install-cert -d adguard.example.com \
--fullchain-file /opt/adguardhome/conf/fullchain.pem \
--key-file /opt/adguardhome/conf/privkey.pem \
--reloadcmd "docker restart adguardhome"12.2. Habilitar no AdGuard
Settings -> Encryption settings:
- Enable encryption (HTTPS, DoH, DoT, DoQ): ON.
- Server name:
adguard.example.com. - HTTPS port: 443.
- DNS-over-TLS port: 853.
- DNS-over-QUIC port: 853 (UDP).
- Redirect HTTP to HTTPS: ON.
- Certificate: caminho pro
fullchain.pemou colar o PEM. - Private key: caminho pro
privkey.pemou colar.
URLs resultantes:
- DoH:
https://adguard.example.com/dns-query - DoT:
tls://adguard.example.com:853 - DoQ:
quic://adguard.example.com:853
12.3. Configurar clientes
Firefox: about:preferences#privacy -> DNS over HTTPS -> Max Protection -> Custom -> https://adguard.example.com/dns-query.
iOS / macOS: usar Mobileconfig generator ou perfil .mobileconfig com DNSSettings.ServerURL.
Android 9+: Settings -> Network -> Private DNS -> Hostname -> adguard.example.com. (Android só faz DoT, não DoH.)
13. Roteador apontando para o AdGuard
No roteador, configure o servidor DHCP para entregar o IP do AdGuard como DNS primário para todos os clientes da LAN.
OpenWRT (LuCI): Network -> Interfaces -> LAN -> DHCP Server -> Advanced -> DHCP-Options: 6,192.168.1.10,192.168.1.11 (primário e secundário).
UniFi: Settings -> Networks -> LAN -> DHCP -> DNS Server -> Auto -> Manual -> 192.168.1.10.
Após mudar, force renovar lease nos clientes:
# Linux
sudo dhclient -r && sudo dhclient
# Windows
ipconfig /release && ipconfig /renew && ipconfig /flushdns
# macOS
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder13.1. Clientes que ignoram o DHCP
Alguns clientes têm DNS hardcoded e ignoram o DHCP:
- Smart TVs (Samsung, LG): hardcoded
8.8.8.8para alguns serviços. - Chromecast: hardcoded
8.8.8.8. - Android com Private DNS: usa DoT direto pra Cloudflare/Google.
- Apps que usam DoH embutido (Firefox padrão).
Solução: transparent DNS redirect no firewall do roteador. OpenWRT exemplo:
# Redireciona qualquer query DNS (UDP/TCP 53) saindo da LAN
# para o AdGuard local
iptables -t nat -A PREROUTING -i br-lan -p udp --dport 53 -j DNAT \
--to-destination 192.168.1.10:53
iptables -t nat -A PREROUTING -i br-lan -p tcp --dport 53 -j DNAT \
--to-destination 192.168.1.10:53
# Bloqueia DoT (porta 853) saindo da LAN para forçar uso do AdGuard
iptables -A FORWARD -i br-lan -p tcp --dport 853 -j REJECT
iptables -A FORWARD -i br-lan -p udp --dport 853 -j REJECTDoH é mais difícil de bloquear (vai em HTTPS porta 443), só dá pra bloquear via lista de IPs conhecidos de DoH (Mozilla mantém uma).
14. WireGuard apontando para o AdGuard
No servidor WireGuard, garanta que o AdGuard escuta na interface wg0 também (All interfaces no DNS settings já cobre).
Na configuração do peer cliente:
[Interface]
PrivateKey = ...
Address = 10.0.0.2/32
DNS = 10.0.0.1 # IP do servidor WG, onde o AdGuard responde
[Peer]
PublicKey = ...
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25Importante:
DNS = 10.0.0.1força o cliente WG a usar o AdGuard via VPN, mesmo em rede pública.- No AdGuard, garanta que o IP do peer (
10.0.0.2) está na ACL ou emSettings -> DNS settings -> Access settings -> Allowed clients(se você restringiu). - No firewall do servidor VPN, permitir input UDP/TCP 53 na interface
wg0:iptables -A INPUT -i wg0 -p udp --dport 53 -j ACCEPT iptables -A INPUT -i wg0 -p tcp --dport 53 -j ACCEPT
Validar do lado do cliente VPN conectado:
dig @10.0.0.1 github.com
dig @10.0.0.1 gitea.lan.example # resolve split-horizon15. Alta disponibilidade com 2 instâncias
Topologia simples:
Roteador DHCP -> entrega DNS = 192.168.1.10, 192.168.1.11
^ ^
AdGuard AdGuard secundário
(Docker) (Raspberry Pi)Clientes do sistema operacional fazem failover automático: se primário não responde em ~3s, tentam secundário.
15.1. Sincronizar configs
Manualmente, com rsync cron:
# No primário, cron diário:
rsync -av /opt/adguardhome/conf/AdGuardHome.yaml \
admin@192.168.1.11:/opt/AdGuardHome/AdGuardHome.yaml
ssh admin@192.168.1.11 'sudo systemctl restart AdGuardHome'Ou versionar o yaml em git (ex: instância Gitea interna):
cd /opt/adguardhome/conf
git init
git remote add origin git@gitea.example.internal:infra/adguard-config.git
git add AdGuardHome.yaml
git commit -m "config snapshot"
git pushCuidado: o yaml contém o hash bcrypt da senha admin. Tudo bem versionar em repo privado. Se for público, sanitize antes.
15.2. AdGuard Home Sync (ferramenta dedicada)
adguard-sync — daemon que sincroniza configuração de primário para N réplicas via API. Mais robusto que rsync.
services:
adguardhome-sync:
image: ghcr.io/bakito/adguardhome-sync:latest
container_name: adguardhome-sync
restart: unless-stopped
volumes:
- ./adguardhome-sync.yaml:/config/adguardhome-sync.yaml
command: run --config /config/adguardhome-sync.yaml15.3. keepalived (VIP)
Se quiser um único IP virtual (VIP) que floats entre primário e secundário:
# /etc/keepalived/keepalived.conf no PRIMÁRIO
vrrp_instance VI_DNS {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass changeme
}
virtual_ipaddress {
192.168.1.9/24
}
}Secundário com state BACKUP e priority 90. DHCP entrega 192.168.1.9 como DNS, keepalived garante que sempre tem alguém respondendo.
Para um laboratório doméstico, DHCP entregando os dois IPs resolve sem a complexidade do keepalived.
16. Backup
Arquivos a backupear:
/opt/adguardhome/conf/AdGuardHome.yaml— toda a configuração (filtros, rewrites, upstreams, encryption, clients, senhas hash)./opt/adguardhome/work/data/— query log, stats, sessions, filters baixados.data/querylog.json/data/querylog.json.1— log de queries.data/stats.db— estatísticas agregadas.data/sessions.db— sessões web.data/filters/— blocklists cacheadas (regerável, opcional no backup).
Cron diário com restic ou tar:
#!/bin/bash
# /opt/adguardhome/backup.sh
DATE=$(date +%Y%m%d)
tar czf /var/backups/adguard-${DATE}.tar.gz \
-C /opt/adguardhome conf/AdGuardHome.yaml work/data
find /var/backups -name 'adguard-*.tar.gz' -mtime +30 -deleteRestore: parar o serviço, extrair, restart.
Versionar o yaml em git é o backup mais barato e útil para revisão de mudanças.
17. Estatísticas e logs
17.1. Query log
Query log no menu lateral. Filtros:
- Client: filtrar por IP/cliente nomeado.
- Domain: regex parcial.
- Status:
Processed,Blocked,Whitelisted,Rewritten. - Reason:
FilteredBlackList,Rewrite,NotFilteredNotFound, etc.
Útil para auditoria (“por que o Smart TV não conectou no Netflix? aha, blocklist X matou o domain”). Exportável em JSON.
Retenção configurável em Settings -> General -> Logs configuration. 7 dias é suficiente para troubleshooting; 30+ dias gera GB.
17.2. Statistics dashboard
Dashboard (página inicial). Mostra:
- Queries totais nas últimas 24h / 7d / 30d.
- % bloqueadas.
- Top clients por volume.
- Top queried domains.
- Top blocked domains.
- Upstream response time.
Útil para identificar smart devices barulhentos (smart TV consultando 5000 hosts/dia é sinal de telemetria agressiva).
17.3. Métricas externas (Prometheus)
AdGuard Home não expõe /metrics nativo. Use um exporter externo:
- adguardhome-exporter — scrape via API e expõe em formato Prometheus.
services:
adguard-exporter:
image: ebrianne/adguard-exporter:latest
ports: ["9617:9617"]
environment:
- ADGUARD_PROTOCOL=http
- ADGUARD_HOSTNAME=192.168.1.10
- ADGUARD_USERNAME=admin
- ADGUARD_PASSWORD=changemePlug no Grafana com dashboard pronto (id 13330).
18. Troubleshooting
18.1. Porta 53 já está em uso
Sintoma: AdGuard falha ao subir com erro bind: address already in use.
sudo ss -lnup | grep :53
sudo ss -lntp | grep :53Quase sempre é systemd-resolved. Solução (escolha uma):
# A) Desabilitar completamente
sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
# B) Manter resolved mas tirar do 53
sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
sudo rm /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.confEm Docker, garanta network_mode: host ou que a porta 53 está mapeada e livre no host.
18.2. Clientes não veem o AdGuard
Sintomas: queries não aparecem no log, clientes ainda resolvem via Cloudflare/ISP.
Causas possíveis:
- DHCP do roteador ainda entrega DNS antigo. Force renovação:
ipconfig /release && ipconfig /renew && ipconfig /flushdns(Win),sudo dhclient -r && sudo dhclient(Linux). - Cliente tem DNS configurado manualmente (override do DHCP). Verifique:
- Win:
ipconfig /all - Linux:
resolvectl status - macOS:
scutil --dns
- Win:
- Cliente usa DNS hardcoded (Chromecast, smart TV). Aplicar redirect transparente no firewall (ver 13.1).
- Cliente usa DoH/DoT externo (Firefox padrão usa Cloudflare DoH). Desabilitar em
about:config: network.trr.mode = 5ou apontar pro seu próprio DoH.
18.3. DoH falhando
Sintomas: Certificate verify failed, SSL handshake error.
- Certificado expirado:
openssl x509 -in /opt/adguardhome/conf/fullchain.pem -noout -dates. - SAN errado: certificado emitido pra
adguard.example.commas cliente conectaadguard.lan.example. Reemita com SAN correto. - Cadeia incompleta: garanta que está usando
fullchain.pem(cert + intermediários), não sócert.pem. - Cliente não confia na CA: se for cert self-signed ou de CA interna, importar a CA no trust store do cliente.
18.4. Domínios internos (.lan, .internal, etc) não resolvem
Sintomas: dig gitea.lan.example retorna NXDOMAIN.
Causas:
- DNS rewrite não configurado. Veja seção 10.1.
- Conditional forwarding pra zona interna não está apontado pro DNS interno correto. Veja 10.3.
- Sem rewrite e sem forwarding, AdGuard manda pra upstream público, que rejeita TLDs privados (TLDs
.lan,.home,.local,.internalsão reservados ou tratados de forma especial —.localcolide com mDNS).
Recomendação: não use .local (conflita com mDNS/Bonjour). Use .lan, .internal, ou um subdomínio real sob um domínio que você controla (lab.example.com).
18.5. Upstream timeout
Sintomas: queries demoram 5s+, log mostra upstream exchange timeout.
- Firewall bloqueando saída: garanta que UDP/TCP 53 saindo está liberado.
- IPv6 desabilitado no SO mas habilitado no AdGuard: o AdGuard tenta AAAA upstream, que falha. Desabilite IPv6 no AdGuard (
Settings -> DNS -> Disable IPv6 resolution). - Unbound mal configurado:
sudo systemctl status unbounde checar logs.sudo unbound-checkconfpara validar sintaxe da config. - Root hints faltando:
sudo apt install dns-root-data.
18.6. AdGuard cai sob carga
Sintomas: UI fica lenta, queries dropam.
- Rate limit muito baixo: aumentar (ver 8.2).
- Query log muito grande: rotacionar mais cedo (
Settings -> Logs -> Retention). - Filesystem cheio:
df -h /opt/adguardhome/work. - Container sem memória: ajustar
mem_limitno compose.
18.7. Listas não atualizam
Filters -> DNS blocklists -> Check for updates. Se falhar:
- DNS do próprio host quebrado (AdGuard usa upstream pra resolver os hostnames das blocklists). Configurar bootstrap DNS com IPs puros.
- HTTPS bloqueado: verificar com
curl -v https://big.oisd.nl/. - URL da lista mudou (acontece com Hagezi às vezes). Remover e re-adicionar com URL nova.
19. Referências
- AdGuard Home — repo: https://github.com/AdguardTeam/AdGuardHome
- AdGuard Home — Wiki (legacy): https://github.com/AdguardTeam/AdGuardHome/wiki
- AdGuard Home — Knowledge Base (atual): https://adguard-dns.io/kb/adguard-home/
- Docker Hub: https://hub.docker.com/r/adguard/adguardhome
- Pi-hole Unbound guide: https://docs.pi-hole.net/guides/dns/unbound/
- Unbound docs: https://nlnetlabs.nl/documentation/unbound/
- DNS Flag Day 2020 (EDNS buffer 1232): https://dnsflagday.net/2020/
- OISD blocklist: https://oisd.nl/
- Hagezi blocklists: https://github.com/hagezi/dns-blocklists
- Steven Black hosts: https://github.com/StevenBlack/hosts
- AdGuard sync (HA): https://github.com/bakito/adguardhome-sync
- AdGuard exporter (Prometheus): https://github.com/ebrianne/adguard-exporter
- Quad9 DoT/DoH: https://www.quad9.net/service/service-addresses-and-features
- Cloudflare 1.1.1.1 DoT/DoH: https://developers.cloudflare.com/1.1.1.1/encryption/
- Blocky (alternativa): https://github.com/0xERR0R/blocky
- Technitium DNS (alternativa): https://technitium.com/dns/