systemd-resolved 和 dnsmasq 抢 53 端口

想在机器上跑个 dnsmasq 给内网做解析,装完 systemctl start dnsmasq 直接失败,日志只有一句 failed to create listening socket for port 53。

先看是谁占着

sudo ss -lunp | grep ':53'
# UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=756,fd=17))
# UNCONN 0 0      127.0.0.54:53 0.0.0.0:* users:(("systemd-resolve",pid=756,fd=19))

两个监听:127.0.0.53 是给本机应用用的 stub, 127.0.0.54 是给 systemd-networkd 做 DNS 代理的。 只要 resolved 在跑,这两条就占着。

三种处理

1. 关掉 stub listener(改配置)

最温和,保留 resolved 的其他功能,只放开 127.0.0.53:53:

# /etc/systemd/resolved.conf
[Resolve]
DNS=223.5.5.5 119.29.29.29
DNSStubListener=no
sudo systemctl restart systemd-resolved

注意 127.0.0.54:53 那条还在,但 dnsmasq 默认只绑 0.0.0.0, 会跟它冲突。给 dnsmasq 加一句 bind-interfaces 和明确的 listen-address 就行。

2. 直接停掉 resolved

sudo systemctl disable --now systemd-resolved
sudo rm -f /etc/resolv.conf
printf 'nameserver 127.0.0.1\n' | sudo tee /etc/resolv.conf

干净,但要记得 /etc/resolv.conf 原本是指向 /run/systemd/resolve/stub-resolv.conf 的符号链接, 删之前确认一下:

ls -l /etc/resolv.conf

删了软链后有些工具(比如 networkd 相关的)会再把它重建回来, 所以还要 chattr +i 或者干脆别删。

3. 让 dnsmasq 换个端口

如果只是想让内网机器用,不一定非要 53,port=5353 也能工作, 反正上游可以在别处配。这种方式对系统零侵入,我最常用。

顺带说个坑

dig 查本机解析时结果和 getent hosts 不一致, 是因为它们走不同的路径:dig @127.0.0.1 直连 dnsmasq, 而 getent 走 /etc/nsswitch.conf 加 /etc/resolv.conf。排查「解析不对」之前先分清是哪一层。

getent hosts example.com
dig +short example.com @127.0.0.1
resolvectl query example.com

三条命令跑一遍,问题基本就定位了。

Linux DNS systemd