sing-box 1.12 正式版是一次 DNS 与规则系统的深层重构,而不是常规的 patch 迭代。如果你还在使用旧版 sing-box 内核,升级 1.12 的第一步不是替换二进制文件,而是先打开 dns 和 route 两段 JSON 配置逐项排查:有没有 geoip:cn、geosite:gfw、address: "tls://1.1.1.1" 这类写法。这些在 1.12 里要么已经报错,要么正在输出 deprecated 日志,只是还没到强制拒绝的阶段。
DNS server 新格式:address 字符串改成 type 结构体,要改什么?
旧版 DNS server 的典型写法是把协议和地址拼成一行字符串,比如 "address": "tls://1.1.1.1"、"address": "https://dns.google/dns-query"、"address": "fakeip"。1.11 之前这种写法是主流也是默认示例。
1.12 把 DNS server 拆成了显式 type 结构体:type: udp、type: tls、type: https、type: fakeip、type: h3 等,地址和服务参数分别写进不同字段。旧写法还能加载,但从 1.12 起启动日志里会出现 deprecated: use type field for DNS server 这类警告。官方的迁移截止版本是 1.14,届时旧写法直接启动失败。
下面是两种写法的对照:
| DNS 用途 | 1.11 旧写法 | 1.12 新写法 |
|---|---|---|
| UDP 上游 DNS | "address": "1.1.1.1" | "type": "udp", "server": "1.1.1.1" |
| DNS over TLS | "address": "tls://1.1.1.1" | "type": "tls", "server": "1.1.1.1" |
| DNS over HTTPS | "address": "https://dns.google/dns-query" | "type": "https", "server": "dns.google" |
| Fake-IP | "address": "fakeip" | "type": "fakeip" |
| 带 tag 的内部分组 | "tag": "remote-dns", "address": "tls://8.8.8.8" | "tag": "remote-dns", "type": "tls", "server": "8.8.8.8" |
实际场景是你维护了一份给别人用的模板配置,里面写了四五个 DNS server,升级 1.12 后客户端用户反馈 “DNS 查询偶尔很慢”。打开日志一看几十行 deprecated 警告,不是在报错,但每次 DNS 查询都要走兼容转换路径,拖慢了首次解析。
domain_resolver 替代旧 outbound DNS rules:多出站用户最容易漏
这一项改动的影响面比 DNS server 格式更大,因为它改的不是写法而是逻辑层级。
旧逻辑是把 “某个 outbound 用什么 DNS” 写在 dns.rules 里:匹配 outbound == "proxy" 这条 rule,再指定 server。新逻辑把这个决策移到 outbound 自己的拨号配置 dial_fields.domain_resolver,或者全局 route.default_domain_resolver。一次出站拨号触发 DNS 解析时,不再去 dns.rules 里找匹配规则,而是直接看该 outbound 绑定的 resolver tag。
常见踩坑场景:你有一个直连 outbound 和一个代理 outbound,旧配置在 dns.rules 里按 outbound 做 rule_set 匹配。升级 1.12 后直连的 DNS 能解析,代理的 DNS 全部超时——因为 1.12 找不到对应 domain_resolver 时,不会自动 fallback 到旧 rule,而是回到系统默认 DNS,恰好你的代理 outbound 访问不了系统 DNS。
改法分两步:
- 在
dns里定义两个 server:local-dns用type: udp指本地,remote-dns用type: https指远程。 - 在 outbound 的
dial_fields里写"domain_resolver": "remote-dns"。全局默认则在route下加"default_domain_resolver": "local-dns"。
| 旧写法位置 (1.11) | 新写法位置 (1.12) | 注意 |
|---|---|---|
dns.rules[].outbound 匹配 + server | outbound 的 dial_fields.domain_resolver | 每个出站单独声明 |
| 无全局默认 | route.default_domain_resolver | 兜底所有没声明 domain_resolver 的出站 |
dns.rules[].domain_strategy | outbound 的 domain_strategy | 字符串名没变,但层级从 dns 移到 outbound |
GeoIP/Geosite 彻底移除:为什么 .srs rule-set 更快?
GeoIP 和 Geosite 从 sing-box 1.8 起就标记为 deprecated,但内核里一直保留兼容,1.11.x 的配置里写 geoip:cn 或者 geosite:geolocation-!cn 仍然能跑。1.12 把兼容代码全部移除了,启动时遇到这两个字段直接报 unknown rule item type,配置拒绝加载。
这件事对性能的影响比很多人预想的大。旧的 geoip.dat 和 geosite.dat 是 V2Ray 年代的文本格式,sing-box 每次启动要解析整个 dat 文件,规则越多、解析越慢。1.12 强制切换到 .srs 二进制 rule-set 文件后,读取方式从全文解析变成了 mmap 内存映射 + 本地缓存,对几百条规则集的配置,启动时间从 3-5 秒缩短到 0.5-1.5 秒是实测可复现的范围。
迁移的完整步骤:
- 下载对应
.srs文件(从 rule-set 官方仓库或自生成),放进配置目录下的rule-set/文件夹。 - 在
route.rule_set里声明本地路径,例如"type": "local", "path": "rule-set/geoip-cn.srs", "tag": "geoip-cn"。 - 在
route.rules里用rule_set: geoip-cn替代原来的geoip:cn。 - 同理,
dns.rules里也改成rule_set引用。
| 旧字段 (1.11 及之前) | 新字段 (1.12) | 文件格式 |
|---|---|---|
geoip:cn | rule_set: geoip-cn | .srs 二进制 |
geosite:category-ads-all | rule_set: geosite-category-ads-all | .srs 二进制 |
geosite:gfw | rule_set: geosite-gfw | .srs 二进制 |
geoip:private | ip_is_private: true(内建) | 不再需要外部文件 |
如果你在路由器或低配 NAS 上跑 sing-box,切到 .srs 之后规则加载的内存占用也有明显下降:旧的 dat 解析需要在堆上分配完整中间结构,.srs 直接 mmap 映射文件,规则匹配时只读被查询的那一段。
1.12 完整变更速览
DNS 和规则之外,1.12 还塞了不少协议层和平台层的改动。下面是按影响面排序的变更表:
| 类别 | 变更 | 谁需要关注 |
|---|---|---|
| DNS | DNS server 格式重构,旧写法 deprecated | 所有自维护 JSON 配置用户 |
| DNS/Route | domain_resolver 替代旧 outbound DNS rules | 多出站配置用户(直连+代理混用) |
| 规则 | GeoIP/Geosite 彻底移除 | 所有使用 geoip/geosite 的配置 |
| 规则 | .srs rule-set 二进制格式强制 | 所有使用规则集的配置 |
| 协议 | AnyTLS 新协议 | 需要降低 TLS 指纹识别风险的场景 |
| 协议 | ShadowTLS wildcard SNI 支持 | 自建 ShadowTLS 入站的用户 |
| 协议 | NTP 协议 sniffer | 需要识别 NTP 流量的旁路由场景 |
| 平台 | Apple TUN 性能提升最高 2 倍+ | iOS/macOS 用户 |
| 平台 | 新增 Tailscale endpoint 与 DNS server | 混合使用 Tailscale 与 sing-box 的用户 |
| 平台 | DERP 服务内建 | 自建 Tailscale DERP 中继节点的用户 |
| 平台 | resolved service (systemd-resolved DBUS) | Linux 桌面用户,NetworkManager 接管 DNS |
| 监听 | bind_interface、routing_mark、reuse_addr | 多网卡、多路由表环境 |
| 加密 | ECH 迁移到 Go stdlib 实现 | 依赖 ECH 的配置需重新测试兼容性 |
| 依赖 | Go 最低版本升至 1.23,quic-go v0.52.0,gVisor 20250319.0 | 自行编译的用户 |
从 1.11 升级 1.12,哪些配置项不查会直接报错?
升级不是替换一个二进制文件就完事。以下清单按检出概率从高到低排列,建议先备份当前配置文件和 core 版本号,再逐项过。
| 检查项 | 怎么看 | 改法 |
|---|---|---|
| DNS server 是否还在用 address 字符串 | 搜索 "address": + 包含 :// 的行 | 改为 type + server 分离写法 |
| route.rules 里有没有 geoip: / geosite: | 搜索 geoip: 和 geosite: | 全部替换为 rule_set 引用 + .srs 文件 |
| dns.rules 里有没有按 outbound 匹配 DNS server | 搜索 dns.rules 中 outbound 条件 | 移到对应 outbound 的 dial_fields.domain_resolver |
| 是否有多出站但没有 default_domain_resolver | 检查 route 段 | 加 "default_domain_resolver": "<tag>" |
| rule-set 文件是不是 .json/.yaml 文本 | 检查 rule_set 目录下文件后缀 | 替换为 .srs 二进制文件 |
| 是否引用了不再存在的旧 route rule action | 启动日志搜索 deprecated、unknown field | 对照官方 Migration 改字段名 |
| 自编译用户 Go 版本是否 ≥ 1.23 | go version | 升级 Go 工具链 |
建议先在测试 profile 或 staging 环境验证,不要直接覆盖生产配置。命令行用户至少保留 sing-box version 输出和旧 JSON 的副本;SFA 图形客户端用户先导出当前配置文件再操作。
哪些场景可以先不升级?
1.12 不是安全修复版本,不涉及 CVE 修补。如果你的配置没有使用 geoip/geosite、没有多出站混用 DNS 策略、也没有自编译需求,继续用 1.11.x 的生产配置短期内不会出问题。
但官方的 deprecated 移除计划是 1.14 即一刀切:1.12 兼容旧写法(输出警告)、1.13 兼容旧写法(输出警告)、1.14 直接拒绝加载。如果你计划长期使用 sing-box 内核,DNS server 格式和 rule-set 迁移早晚要做,在 1.12 阶段动手比等到 1.14 配置被拒绝时手忙脚乱要好。