Files
server-deploy/vps-xray/README.md
T
2026-08-07 11:47:52 +08:00

452 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Xray VPS 部署指南
> 一键部署 Xray 代理,支持 VLESS-Reality(抗封锁)和 VLESS TCP(极速)两种模式。无需域名和证书。
## 功能特性
- 两种部署模式可选:Reality(推荐)/ Fast TCP
- 自动安装 Xray + 生成密钥 + 配置防火墙
- BBR 拥塞控制 + TCP 深度调优
- 自动选择延迟最低的 Reality 伪装目标
- 部署完成自动输出 VLESS 分享链接
- 一键备份 / 卸载脚本
## 模式对比
| 特性 | Reality(推荐) | Fast TCP |
|------|-----------------|----------|
| **加密** | Reality TLS 1.3 | 无,明文传输 |
| **抗检测** | 伪装为真实 HTTPS 流量 | 极易被 DPI 识别 |
| **需要域名** | 否 | 否 |
| **需要证书** | 否 | 否 |
| **速度** | Vision 流控避免双重加密,差距 < 5% | 裸 TCP 无开销,理论最快 |
| **被封风险** | 极低 | 极高 |
## 技术栈
| 组件 | 说明 |
|------|------|
| Xray | VLESS 代理核心 |
| BBR | TCP 拥塞控制算法 |
| ufw / firewalld | 防火墙 |
## 前置条件
1. 一台境外 Linux VPSDebian 12 推荐)
2. root 权限
3. 服务器 443 端口可从外网访问
### 推荐配置
| 项目 | 最低 | 推荐 |
|------|------|------|
| CPU | 1 核 | 1 核 |
| 内存 | 256 MB | 512 MB |
| 硬盘 | 5 GB | 10 GB |
| 带宽 | 200 Mbps | 1 Gbps |
| 流量 | 500 GB/月 | 1 TB+/月 |
| 位置 | — | 日本 / 新加坡 / 美西 |
## 目录结构
```
vps-xray/
├── deploy.sh # 一键部署脚本
├── uninstall.sh # 完全卸载脚本
├── backup.sh # 备份脚本
├── upload.sh # 上传脚本(仅上传服务端必要文件)
├── .env.example # 配置模板
├── README.md # 本文件
├── vps-xray-optimized.md # Reality 方案详细文档
├── vps-xray-fast.md # Fast TCP 方案详细文档
└── client-config/ # 客户端配置(本地使用,不上传服务器)
├── gen-clash.sh # 由节点信息生成 Clash.Meta 配置
└── client-macos.md # macOS 客户端指南(模板,可提交)
```
> `client-config/` 下含真实 UUID / 服务器 IP 的节点配置(`*-info*.md`、`*.yaml`)已在根 `.gitignore` 中排除,不要提交到版本库。
服务器上的文件位置:
```
/usr/local/bin/xray # Xray 可执行文件
/usr/local/etc/xray/config.json # Xray 配置文件
/etc/sysctl.d/99-xray-turbo.conf # 网络调优参数
/var/backups/xray/ # 备份文件
```
## 快速部署
### 第一步:上传文件到 VPS
使用 `upload.sh` 只上传服务端必要文件(`deploy.sh``uninstall.sh``.env.example`),跳过文档和客户端配置:
```bash
# 在本地 vps-xray/ 目录下执行
bash upload.sh <VPS_IP>
```
### 第二步:登录 VPS 执行部署
```bash
ssh root@<VPS_IP>
cd /opt/vps-xray
# Reality 模式部署(推荐)
bash deploy.sh
# 或者 Fast TCP 模式
bash deploy.sh --mode fast
```
部署完成后会输出:
- 连接参数(IP、端口、UUID、密钥等)
- VLESS 分享链接(可直接导入客户端)
凭据保存在 `/opt/vps-xray/.env`(权限 600)。**信息不会丢** —— 随时可以重新打印:
```bash
cd /opt/vps-xray && bash deploy.sh --show
```
`--show` 只读取 `.env` 输出连接信息和分享链接,不安装、不改配置、不重启服务。
> 即使 `.env` 被误删,只要 `/usr/local/etc/xray/config.json` 还在,UUID、shortId、SNI 都能从中读出,公钥可用 `xray x25519 -i <privateKey>` 从私钥反推。
### 第三步:客户端配置
将部署脚本输出的 `vless://` 链接导入客户端即可:
| 平台 | 推荐客户端 |
|------|-----------|
| Windows | v2rayN6.x+ |
| macOS | V2BOX / NekoRay,详见 [client-macos.md](client-macos.md) |
| iOS | Shadowrocket / Streisand |
| Android | v2rayNG |
#### 生成 Clash 配置
把每台 VPS 的 `bash deploy.sh --show` 输出保存成 `client-config/xxx-info.md`,然后:
```bash
cd client-config
bash gen-clash.sh # 交互式勾选要写入的节点
bash gen-clash.sh --all # 全部节点
```
脚本解析文件里的 `vless://` 链接,生成 `vpn-vps-xray-optimized.yaml`(多节点自动组成 url-test 自动选择组,原文件会先备份为 `.bak`)。
> 生成的配置需要 **Clash.Meta / mihomo** 内核(Clash Verge Rev、ClashX Meta、FlClash 等)。原版 Clash / Clash Premium 不支持 VLESS + Reality。
详细的 Clash Meta / Sing-Box 配置参见 [vps-xray-optimized.md](vps-xray-optimized.md)。
## 日常运维
### 查看连接信息 / 分享链接
```bash
cd /opt/vps-xray
bash deploy.sh --show
```
只读操作,不会改动任何服务。
### 更换 Reality 伪装目标
伪装目标在首次部署时按握手延迟自动挑选,之后固定写入 `.env` 不再变动。后续每次部署会检查它是否仍然可达,不可达时告警但**不会自动更换**——因为更换会改变 SNI,导致所有客户端链接静默失效。
确认要换时显式执行:
```bash
bash deploy.sh --redetect
```
> ⚠️ 换完后 SNI 变了,**所有客户端都必须用新的分享链接重新配置**。脚本会在更换时打印醒目提示。
#### 自定义伪装目标时的两个硬条件
`.env` 里手动指定 `REALITY_DEST` / `REALITY_SNI` 前,必须确认新域名同时满足:
**1. 技术条件** — TLS 1.3,且证书链总长 < 8192 字节。上限是 `xtls/reality` 里的硬编码值,超出会直接握手失败(见 [XTLS/Xray-core#6356](https://github.com/XTLS/Xray-core/issues/6356)`www.microsoft.com` 的 8273 字节即触发)。在服务器上自查:
```bash
xray tls ping www.apple.com # 只看「Pinging with SNI」那一段
```
**2. 客户端路由条件** — 该域名必须会被客户端判成「直连」。
这条不直观。TUN 模式下客户端偶尔会把「连接本代理服务器」这件事本身也抓进代理(v2rayN 的 sing-box + xray 双核心靠进程嗅探防回环,Windows 上存在竞态)。这种回环连接的去向完全由 SNI 决定:
| SNI 被判为 | 结果 |
|---|---|
| 直连 | 连接真的到达本服务器,REALITY 握手成功,**用户无感** |
| 走代理 | 进隧道 → 服务器替客户端连了真正的伪装站 → 客户端收到真证书 → 刷屏 `REALITY: received real certificate` |
实测(v2rayN 默认规则模板,`geosite:google → proxy` 排在 `geosite:cn → direct` 之前):
| 域名 | 命中规则 | 可用性 |
|---|---|---|
| `www.apple.com``*.apple.com` | `geosite:cn` | ✅ 安全 |
| `dl.google.com` | `geosite:google` | ❌ 不安全 |
| `www.microsoft.com` | 都不命中 → `final:proxy` | ❌ 不安全 |
| `www.amazon.com` | 都不命中 → `final:proxy` | ❌ 不安全 |
脚本的候选列表因此只保留 Apple 系域名。
### 定时重启
默认**关闭**。原因是:
- 崩溃恢复已由 systemd 的 `Restart=always` + `RestartSec=3` 覆盖(秒级响应),不必等到凌晨
- Xray 无已知需要定期重启的缺陷(内存、句柄上限均已配置)
- 而硬重启会切断全部活动连接——跨夜下载、备份、CI 都会中断
即"为假想问题付出真实代价"。仅在确实观察到长期运行后性能退化时才启用。
需要时用轻量入口调整,**不会重装 Xray、不会重写配置、不会重启服务,现有 vless 链接完全不受影响**:
```bash
bash deploy.sh --restart 7 04:00 # 每 7 天 04:00 重启
bash deploy.sh --restart 3 # 每 3 天,时间沿用 .env
bash deploy.sh --restart 0 # 关闭
bash deploy.sh --restart # 按 .env 现有值重建 timer
```
对应 `.env` 三个字段:
| 字段 | 含义 | 默认 |
|---|---|---|
| `XRAY_RESTART_EVERY_DAYS` | 0 = 关闭;N = 每 N 天 | `0` |
| `XRAY_RESTART_TIME` | 24 小时制 `HH:MM` | `04:00` |
| `XRAY_RESTART_TIMEZONE` | 时区 | `Asia/Shanghai` |
#### 查看当前配置与重启记录
**一条命令看全** —— 不带参数的 `--restart` 只按 `.env` 现值重建 timer,不改配置、不重启服务:
```bash
bash deploy.sh --restart
```
输出里包含当前间隔与时间、**上次实际重启**、**下次实际重启**。
分项查看:
```bash
# 1. 配置值
grep '^XRAY_RESTART_' /opt/vps-xray/.env
# 2. timer 是否启用、下次唤醒时间
systemctl list-timers xray-restart.timer
# 3. 确认时区真的生效(最容易出错的一项)
systemctl show xray-restart.timer -p TimersCalendar
# 4. 上次实际重启的时间戳
cat /var/lib/xray/last-restart # Unix 时间戳
date -d "@$(cat /var/lib/xray/last-restart)" # 可读格式
# 5. 守卫每次唤醒后的判断记录(跳过还是执行)
journalctl -u xray-restart.service --no-pager -n 50
# 6. xray 进程最近一次启动的时刻
systemctl show xray -p ActiveEnterTimestamp --value
```
第 3 条的输出应形如:
```
{ OnCalendar=*-*-* 04:00:00 Asia/Shanghai ; next_elapse=Fri 2026-08-07 20:00:00 UTC }
```
**里面必须看得到时区。** 若只有 `OnCalendar=*-*-* 04:00:00` 而没有时区,说明时区没生效,systemd 会按服务器的系统时区解析——多数 VPS 是 UTC,那样设的「04:00 北京时间」实际会在**中午 12 点**触发,偏移 8 小时且没有任何提示。
> 早期实现把时区写成 `[Timer]` 段的 `TimeZone=`,而 `systemd.timer` 根本没有这个 key——写了只会在 journal 里留一行 `Unknown key name 'TimeZone' in section 'Timer', ignoring.` 然后按本地时区解析。排查时可以顺手确认一下:
>
> ```bash
> journalctl -b | grep -i "unknown key name"
> systemd-analyze verify /etc/systemd/system/xray-restart.timer
> ```
第 5 条的日志长这样:
```
xray-restart-guard[1234]: 距上次重启不足 7 天(已过 3 天),跳过
xray-restart-guard[5678]: 距上次重启已满 7 天,执行重启
```
出现下面这行说明状态文件写不进去(磁盘满或只读挂载),守卫会主动跳过本轮——宁可不重启,也不让保护失效后退化成每天硬重启:
```
xray-restart-guard[...]: 无法写入 /var/lib/xray/last-restart,跳过本轮重启以免退化成每日重启
```
> ⚠️ `list-timers` 的 `NEXT` 是 **timer 下次「唤醒」的时间,不是下次重启的时间**。timer 每天唤醒一次,NEXT 因此恒为次日的 `XRAY_RESTART_TIME`;是否真的重启由守卫按间隔判断。设了「每 7 天」且 5 天前刚重启过时,NEXT 显示明天凌晨,实际还要再等 2 天。想看真实的下次重启时间,用上面那条不带参数的 `bash deploy.sh --restart`。
#### 为什么不是纯 systemd timer
`OnCalendar` 表达不了任意 N 天:`*-*-1/7 04:00:00` 是「每月 1、8、15、22、29 号」,**跨月会重置**——29 号到下月 1 号只隔 2~3 天。
所以实现是 timer 每天到点唤醒,由 `/usr/local/bin/xray-restart-guard``/var/lib/xray/last-restart`,满 N 天才真正重启。间隔因此是「距上次实际重启」的真实天数,`N=3``N=7` 同样准确。
具体怎么查看配置与执行记录,见上面的「查看当前配置与重启记录」。
#### 已知取舍
- **间隔锚点是「上次由守卫执行的重启」,不是任何一次 xray 重启。** 完整部署(`bash deploy.sh`)里的 `systemctl restart xray` 不会更新 `/var/lib/xray/last-restart`——设了每 7 天、距上次守卫重启已 6.9 天时跑一次完整部署,次日守卫仍会判定满 7 天而再重启一次。介意的话可在部署后手动执行 `date +%s > /var/lib/xray/last-restart` 把锚点对齐。
- **重启失败时时间戳仍已更新**,本轮被跳过,下一轮照常——这是为了避免陷入每天硬重启的循环。但若时间戳**写不进去**(磁盘满、只读挂载),守卫会跳过本轮重启并在日志里报错,宁可不重启也不退化成每日重启。
> 旧字段 `XRAY_DAILY_RESTART` 已废弃。三个新字段都未设置时它仍作为兼容回退(`true` → 每 1 天,其它 → 关闭),重跑部署后 `.env` 会自动换写成新字段。
### 查看状态 / 日志
```bash
systemctl status xray
journalctl -u xray -f
journalctl -u xray -n 100
```
### 备份
```bash
cd /opt/vps-xray
bash backup.sh
```
备份内容包括:
- Xray 配置文件(`config.json`
- 部署配置(`.env` + 脚本)
- 网络调优参数
备份文件保存在 `/var/backups/xray/`,自动清理 30 天前的旧备份。
### 恢复备份
```bash
# 查看可用备份
ls /var/backups/xray/
# 恢复 Xray 配置
tar xzf /var/backups/xray/xray_config_<日期>.tar.gz -C /usr/local/etc/
# 恢复网络调优
cp /var/backups/xray/sysctl_<日期>.conf /etc/sysctl.d/99-xray-turbo.conf
sysctl --system
# 重启服务
systemctl restart xray
```
### 更新 Xray
```bash
# 备份当前配置
cd /opt/vps-xray && bash backup.sh
# 更新 Xray 核心(配置文件保留)
bash <(curl -fsSL https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh)
# 重启服务
systemctl restart xray
```
### 重启 / 停止
```bash
systemctl restart xray # 重启
systemctl stop xray # 停止
systemctl start xray # 启动
```
### 验证
```bash
# 确认 BBR 启用
sysctl net.ipv4.tcp_congestion_control
# 确认端口监听
ss -tlnp | grep 443
# 测试配置语法
/usr/local/bin/xray run -test -config /usr/local/etc/xray/config.json
```
## 完全卸载
```bash
cd /opt/vps-xray
bash uninstall.sh
```
脚本会**交互式确认**每个危险操作:
| 步骤 | 操作 | 确认方式 |
|------|------|----------|
| 0 | 卸载前备份(可选) | y/N |
| 1 | 停止并禁用 Xray 服务 | 输入 YES |
| 2 | 卸载 Xray 程序 + 删除配置 | 自动 |
| 3 | 移除网络调优配置 | 自动 |
| 4 | 关闭防火墙端口 | 自动 |
| 5 | 删除部署目录 `/opt/vps-xray` | y/N |
**备份目录 `/var/backups/xray/` 始终保留**
## 多用户
在 Xray 配置的 `clients` 数组中添加更多用户:
```bash
vi /usr/local/etc/xray/config.json
```
```json
"clients": [
{ "id": "原始-uuid", "flow": "xtls-rprx-vision" },
{ "id": "新用户-uuid", "flow": "xtls-rprx-vision" }
]
```
生成新 UUID`cat /proc/sys/kernel/random/uuid`
修改后重启:`systemctl restart xray`
## 故障排查
### 客户端连接失败
```bash
# 确认 Xray 运行
systemctl status xray
# 确认端口监听
ss -tlnp | grep 443
# 查看错误日志
journalctl -u xray -n 50
```
### 速度慢
```bash
# 确认 BBR 启用
sysctl net.ipv4.tcp_congestion_control
# 应输出: net.ipv4.tcp_congestion_control = bbr
# 确认网络调优生效
sysctl net.core.rmem_max
# 应输出: net.core.rmem_max = 16777216
```
### IP 被封
Reality 能大幅降低被封概率,但如果 IP 已被墙:
1. 更换 VPS IP
2. 重新部署:`bash deploy.sh`
## 安全说明
- `PrivateKey` 只存在服务端配置中,切勿泄露
- `PublicKey` 是客户端参数,可公开
- `.env` 文件权限已设为 600(仅 root 可读)
- BT 流量已在路由规则中屏蔽,降低 VPS 被投诉风险