起因是家里的电视:开机广告、首页推荐里夹带的广告,还有各种 App 时不时往外报数据。电视上没法装插件,手机 App 里的广告也拦不住,能统一管住所有设备的,只有 DNS 这一层。另一个需求是内网服务越来越多,每次都要输 192.168.10.21:8080 这种地址,想给它们起个好记的名字。

AdGuard Home 正好能同时满足这两件事:它是一个带网页管理界面的 DNS 服务器,支持过滤规则、查询日志、按客户端设置规则,也能做本地域名解析。

放在哪里

DNS 是基础设施,它挂了全家都上不了网,所以我没把它放进 Docker 虚拟机(那台机器时不时要重启升级),而是按LXC 和虚拟机那篇的思路,单独开了一个非特权 LXC:Debian 12 模板,1 核、512M 内存,固定 IP 192.168.10.53,好记。

AdGuard Home 是单个可执行文件,官方提供了打包好的压缩包,装起来很简单:

# 在 LXC 里,先从官方发布页下载 AdGuardHome_linux_amd64.tar.gz
tar -xzf AdGuardHome_linux_amd64.tar.gz -C /opt
cd /opt/AdGuardHome
./AdGuardHome -s install      # 注册成系统服务并启动
./AdGuardHome -s status

# 回到 PVE 宿主机:让这个容器开机自启,并且排在第一个启动
pct set 105 --onboot 1 --startup order=1

装好后浏览器打开 http://192.168.10.53:3000 走初始化向导:管理界面端口我改成了 80,DNS 监听所有网卡的 53 端口,再设置管理员账号和密码。

基本配置

向导完成后,大部分设置都能在网页上改,我主要动了这几处:

  • 上游 DNS:填了路由器(它再转发给运营商的 DNS)和一个 DNS-over-HTTPS 地址;Bootstrap DNS 填路由器,用来解析 DoH 服务器本身的域名。
  • 过滤列表:保留默认的 AdGuard DNS filter,又加了一两个常见的中文规则列表。列表不是越多越好,下文会说。
  • DNS 重写:在“过滤器 → DNS 重写”里加上内网域名,比如 nas.home.example.com 指向 192.168.10.20,也支持 *.home.example.com 这样的通配符。

这些设置最终都写在 /opt/AdGuardHome/AdGuardHome.yaml 里。有时候直接改文件更快,但一定要先停掉服务,否则运行中的程序会用内存里的配置把你的修改覆盖掉。和 DNS 相关的部分大致长这样:

dns:
  bind_hosts:
    - 0.0.0.0
  port: 53
  upstream_dns:
    - 192.168.10.1
    - https://dns.example.com/dns-query
  bootstrap_dns:
    - 192.168.10.1
  ratelimit: 20        # 每个客户端每秒最多 20 个请求,这是默认值
  cache_size: 4194304  # 缓存大小,单位字节

自定义规则用的是类似广告过滤插件的语法:|| 开头表示匹配域名及其所有子域,@@ 开头表示放行,! 开头是注释。

! 额外拦截
||ads.example.com^
||telemetry.example.com^
! 被误杀的,放行
@@||cdn.example.com^

让全家用上

最后一步是在路由器的 DHCP 设置里,把下发的 DNS 服务器改成 192.168.10.53。设备重新连网或者续租后就会生效,在 AdGuard Home 的查询日志里能看到每台设备的请求。

踩过的坑

  1. 一开始偷懒只改了路由器的上游。 最初我没动 DHCP,只是把路由器自己用的 DNS 改成了 AdGuard Home。这样所有请求都由路由器转发过来,日志里只有一个客户端,按设备设置规则也就无从谈起。更糟的是触发了速率限制:全家的请求都算在路由器头上,高峰时一秒钟超过 20 个,部分请求直接被丢掉,表现就是网页时不时打不开。改成 DHCP 直接下发之后,问题就消失了。
  2. 要不要填第二个 DNS。 我一开始在 DHCP 里把路由器填成了备用 DNS,结果发现不少设备并不是“主的不通才用备用”,而是两个轮着用甚至同时问,广告时有时无。最后只留了一个 DNS,靠 LXC 开机自启和它本身的稳定性兜底,同时记好了万一它挂了怎么把 DHCP 改回去。
  3. 规则列表加太多会误杀。 有段时间我加了五六个列表,结果购物 App 的商品链接打不开,某个视频网站的播放器也加载不出来。排查方法是在查询日志里按客户端过滤,找被拦截的请求,再加白名单。现在只保留两三个维护活跃的列表。
  4. 有些设备会绕过它。 手机系统的“私人 DNS”、浏览器里的“安全 DNS”都会直接走加密 DNS,不经过 AdGuard Home。自己的设备关掉就行,其他的就随它了。
  5. 53 端口被占用。 我的 Debian 容器里没有这个问题,但如果装在 Ubuntu 上,systemd-resolved 自带的 DNS 存根会占着 53 端口,需要在 resolved 的配置里设置 DNSStubListener=no 把它让出来,官方文档里有详细步骤。

小结

AdGuard Home 跑了一个多月,那个 LXC 的内存占用一直在一百多兆。查询日志里被拦截的请求占了一成多,大部分来自电视和几个智能家居设备。这也让我开始琢磨:是不是该把这些设备单独隔离到一个网段里去了。