前言

最近我在复用一套 Vultr 自动化部署脚本重建 Xray 服务时,遇到了一个有些反直觉的问题:Clash 规则和 Xray 基础配置都没有变化,节点却在一次重新部署后持续报 REALITY authentication failed

看到“认证失败”,第一反应通常是检查 UUID、Reality 公私钥、Short ID 和 SNI。我也按这个思路逐项核对,但客户端与服务端的参数完全一致,端口和网络也都正常。继续对比运行环境后才发现,真正变化的是部署时重新拉取的 teddysun/xray:latest 镜像,以及镜像内 Xray-core 的默认行为。

这篇文章记录完整的定位过程,也说明为什么一份已经验证过的配置,会在重新拉取镜像后得到不同结果。

结论先行

这次故障不是 Clash 节点参数或 Xray 基础配置写错,而是上游默认行为变化经由可变的 Docker latest 标签进入自动化部署后造成的客户端兼容性问题。

服务端升级到 Xray-core v26.7.11 后,如果 REALITY 配置没有显式填写 minClientVer,Xray 会把最低客户端版本默认设为 26.3.27。与此同时,Mihomo v1.19.25/v1.19.28 在 REALITY ClientHello 中仍声明版本 1.8.2,于是握手在进入 VLESS 层之前就被服务端拒绝,客户端统一报错:

1
REALITY authentication failed

已经实际验证两种修复方式都有效:

  1. 将 Xray-core 回退并固定为 v26.6.27
  2. 保持 Xray-core v26.7.11,在服务端 realitySettings 中显式添加:
1
"minClientVer": "1.8.2"

环境与背景

我的服务端通过 Docker Compose 部署在 Vultr VPS 上,镜像使用 teddysun/xray。部署本身由自编写的 Vultr 自动化脚本完成,因此在创建新服务器或重构服务时会重新拉取镜像。

服务器环境:

  • 操作系统:Ubuntu 22.04.5 LTS(Jammy Jellyfish)
  • Linux kernel 5.15.0-185-generic
  • 架构:x86_64 / linux/amd64
  • Docker Engine 29.1.3,API 1.52
  • Docker Compose 2.40.3
  • containerd 2.2.1
  • runc 1.3.4

客户端环境:

  • Clash Verge Rev v2.5.1
  • Mihomo v1.19.25
  • VLESS + TCP + REALITY + Vision
  • flow: xtls-rprx-vision

服务端故障版本:

1
2
Xray 26.7.11 (Xray, Penetrates Everything.) 50231ea
go1.26.5 linux/amd64

同一套脚本和配置在镜像更新前能够正常工作,重新部署并拉取新镜像后节点却全部失联。这种“配置没变,部署结果变了”的特征,最终成为定位镜像漂移的关键线索。

故障现象

Clash 日志持续出现:

1
2
[TCP] dial GLOBAL 198.18.0.1:58911 --> accounts.example.com:443
error: vps.example.com:2223 connect error: REALITY authentication failed

偶尔还会混有:

1
context deadline exceeded

服务端则表现为:

  • access.log 中没有任何 VLESS 入站记录;
  • error.log 只有 Xray 26.7.11 started
  • 端口、域名解析和 TCP 连通性正常。

这里有两个容易误判的点:

  1. 198.18.0.1 是 Clash/Mihomo Fake-IP 地址,不是服务端地址,也不是故障原因;
  2. access.log 为空并不表示连接没有到达服务器。REALITY 在 VLESS 用户认证之前就拒绝了握手,因此请求不会进入 VLESS access log。

先排除配置错误

客户端和服务端逐项核对后,以下内容全部一致:

项目 Clash/Mihomo Xray 服务端 结果
协议 vless vless 一致
端口 2223 2223 一致
传输层 tcp tcp 一致
Flow xtls-rprx-vision xtls-rprx-vision 一致
SNI www.apple.com serverNames: [www.apple.com] 一致
Reality 目标 客户端无需配置 www.apple.com:443 正常
UUID 已逐字符验证 已逐字符验证 一致
Short ID 偶数长度十六进制 同一原值 一致
Reality 密钥 客户端公钥 服务端私钥推导所得公钥 配套

这一步很重要:REALITY authentication failed 最常见的确是公钥、Short ID、SNI 或 UUID 不一致,但本次并不是这些原因。

版本变化与故障时间线

时间(香港时间) 事件
镜像更新前 自动化部署拉取旧版 teddysun/xray:latest,节点正常
2026-07-11 22:30 左右 Xray-core 提交新的 REALITY 默认最低客户端版本逻辑
镜像更新后 teddysun/xray:latest 指向包含 Xray-core v26.7.11 的新镜像
再次部署时 自动化脚本重新拉取镜像,相同配置开始报认证失败
2026-07-13 完成参数核对、版本对比,并验证回退版本与显式设置 minClientVer 均可恢复

Docker Hub 页面显示 teddysun/xray:latest 在故障前后发生了更新(截图时间为 2026-07-13 13:00):

teddysun/xray latest 镜像更新时间

Compose 文件中的 image: teddysun/xray 等价于使用 teddysun/xray:latestlatest 是一个可以移动的标签,并不代表固定构建。因此 Compose 文件虽然没变,重新拉取时得到的镜像 digest 和 Xray 版本仍可能不同。

还需要注意,标签移动不会自动替换已经运行的容器。真正把新版本带进环境的是新建 VPS、重新执行自动化部署、docker compose pull,或其他会重新拉取并创建容器的流程。

根本原因:minClientVer 的默认值发生变化

Xray-core 在 2026-07-11 的提交 af7eb680 中加入了以下逻辑:

1
2
3
} else {
config.MinClientVer = []byte{26, 3, 27}
}

也就是说,从包含该提交的 Xray-core v26.7.11 开始,即使服务端配置文件没有填写 minClientVer,也不再表示“不限制客户端版本”,而是自动得到:

1
"minClientVer": "26.3.27"

Xray-core 为 REALITY 设置默认 minClientVer

截图右侧的 v26.3.27 · Mar 28 容易造成时间线误读(截图时间为 2026-07-13 13:00)。3 月 28 日发布的是被选作最低版本基线的 26.3.27;真正改变默认行为的提交发生在 7 月 11 日,并进入了 v26.7.11 因此这不是一个从 3 月持续潜伏到 7 月的配置错误,而是 7 月的新默认值通过镜像更新进入了部署环境。

另一方面,Mihomo 当时在 REALITY ClientHello 的 Session ID 中仍硬编码:

1
2
3
hello.SessionId[0] = 1
hello.SessionId[1] = 8
hello.SessionId[2] = 2

服务端将其解释为客户端版本 1.8.2。最终比较结果是:

1
2
3
4
Mihomo 声明版本:1.8.2
Xray 默认最低版本:26.3.27

1.8.2 < 26.3.27

所以 REALITY 握手必然被拒绝,且错误发生在 VLESS 用户认证之前。

相关 issues

最初找到的是 XTLS/Xray-core #6482,创建于 2026-07-12 14:38 UTC,描述的是 XHTTP + REALITY 从 v26.6.27 升级到 v26.7.11 后失效。维护者在回复中直接指向上述提交,并建议通过 minClientVer 调整兼容范围。

更早、也与本次协议组合更一致的是:

  • XTLS/Xray-core #6477:创建于 2026-07-12 03:29 UTC,明确复现 Mihomo + VLESS + TCP/raw + REALITY + Vision 在 v26.7.11 上统一报 authentication failed;回退到 v26.6.27 后恢复。
  • MetaCubeX/mihomo #2967:创建于 2026-07-12 04:38 UTC,进一步指出 Mihomo 将 REALITY 客户端版本硬编码为 1.8.2,而 Xray 新默认下限为 26.3.27

因此,#6482 是同一版本门槛问题在 XHTTP + REALITY 上的表现;本次 VLESS + TCP + REALITY + Vision 最直接对应的是更早的 #6477

修复方案一:回退并固定 Xray 为 26.6.27 版本

这是最适合快速恢复和做单变量验证的方案:不修改任何节点参数,只改变 Xray 版本。

1
2
3
4
services:
xray:
image: teddysun/xray:26.6.27
container_name: xray

应用镜像:

1
2
3
docker compose pull xray
docker compose up -d --force-recreate xray
docker exec xray xray version

修改 Compose 文件后,可以先运行 docker compose config -q 检查 YAML。更换镜像时不能只执行 docker compose restartrestart 只会重启原容器,不会应用新的镜像版本。

确认输出包含:

1
Xray 26.6.27

本次实际验证:回退后,无需修改 Clash 和 Xray 基础配置,REALITY 节点立即恢复。

修复方案二:保留 Xray 26.7.11,显式兼容 Mihomo

在服务端 realitySettings 中加入:

1
"minClientVer": "1.8.2"

完整结构示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
"realitySettings": {
"show": false,
"dest": "www.apple.com:443",
"xver": 0,
"serverNames": [
"www.apple.com"
],
"privateKey": "<REALITY_PRIVATE_KEY>",
"shortIds": [
"<SHORT_ID>"
],
"minClientVer": "1.8.2"
}

重启服务:

1
2
docker compose restart xray
docker exec xray xray version

本次实际验证:Xray 保持 v26.7.11 时,加入 minClientVer: "1.8.2" 同样能够恢复连接。

这里填写 1.8.2,是因为 Mihomo 在 REALITY ClientHello 中实际声明的版本就是 1.8.2minClientVer 表示服务端允许接入的最低客户端版本,因此设置为 1.8.2 已经足够兼容当前 Mihomo。

Issue 中提到的 1.0.0 也能生效,是因为 1.8.2 高于 1.0.0,但它会允许更低版本的客户端接入,放宽范围比实际需要更大。需要强调的是,1.8.21.0.0 都是在主动覆盖 Xray v26.7.11 新增的默认版本限制,只适合作为临时兼容方案,不建议长期使用。更稳妥的做法仍然是等待或更换兼容新版本校验的客户端;在此之前,也可以固定已验证可用的 Xray 版本。

给自动化部署脚本的改进

这次问题最有价值的结论并不是“不要升级”,而是让升级变得可观察、可验证、可回滚。

1. 不在生产部署中使用裸 latest

至少固定版本标签:

1
image: teddysun/xray:26.6.27

对可重复性要求更高时,可进一步固定镜像 digest。

2. 部署完成后记录真实版本

1
2
3
docker exec xray xray version
docker inspect xray --format '{{.Config.Image}}'
docker inspect xray --format '{{json .RepoDigests}}'

自动化日志应保存这三项,避免只知道“部署成功”,却不知道实际运行了哪个构建。

3. 加入协议级健康检查

仅检查容器 running 或端口可连接是不够的。这次故障中 TCP 端口完全正常,失败发生在 REALITY 握手阶段。部署完成后应实际通过目标节点发起一次代理请求,并验证出口或目标响应。

4. 保留上一版本的快速回滚入口

将镜像版本做成脚本变量,例如:

1
XRAY_IMAGE=teddysun/xray:26.6.27

新版本验证失败时只需恢复变量并重构服务,而不必临时修改多处 Compose 内容。

5. 升级时先对比运行版本,而不是首先怀疑密钥

这次最初看到 REALITY authentication failed,自然会优先怀疑 UUID、公钥、Short ID 和 SNI。但当“相同配置此前正常、重新部署后突然失败”时,应该尽早加入以下检查:

1
2
docker exec xray xray version
docker image ls teddysun/xray

版本漂移往往比配置漂移更隐蔽。

最终结论

本次故障链路可以概括为:

1
2
3
4
5
6
7
自动化重新部署
→ 重新拉取 teddysun/xray:latest
→ latest 已包含 Xray-core v26.7.11
→ Xray 未配置 minClientVer 时默认要求 >= 26.3.27
→ Mihomo REALITY ClientHello 声明版本 1.8.2
→ REALITY 在进入 VLESS 前拒绝握手
→ Clash 报 REALITY authentication failed

镜像更新前的成功部署已经说明基础配置本身可用。真正变化的是镜像中 Xray-core 的版本,以及新版本在未显式设置 minClientVer 时采用的默认行为。

最终,两条路径都已验证有效:

  • 回退并固定 teddysun/xray:26.6.27
  • 保留 Xray v26.7.11,显式设置 "minClientVer": "1.8.2"

对于继续使用 Mihomo 客户端的环境,第二种方案允许继续升级 Xray;对于强调默认安全策略、不希望放宽客户端版本门槛的环境,可以先固定 v26.6.27,再根据后续生态兼容情况决定升级策略。

参考资料