自建科学上网网关,最难的不是第一次搭起来,而是搭起来之后怎么长期不出问题。协议要更新、镜像要升级、容器可能悄悄挂掉、磁盘会被日志塞满……2026 年,凡是认真玩自建的人,几乎都绕不开一个答案:Docker Compose。把网关服务声明式地编排起来,配合自动更新和健康检查,就能把「三天两头要登录 VPS 看一眼」变成「几个月不用管」。
这篇文章是运维自动化专题,重点讲三件事:Compose 怎么编排网关、Watchtower 怎么自动更新镜像、healthcheck + autoheal 怎么让容器挂了自动恢复。如果你还没搭过多协议网关,建议先看这篇 Docker 部署科学上网服务全攻略,把 Shadowsocks / Trojan / Hysteria2 的多协议底座搭好,再回来做自动化;本文默认你已经有一个能跑的容器化网关。
一、为什么自建网关要上 Docker Compose
很多人的自建网关是从一行 docker run 开始的:
docker run -d --name xray \
-v /etc/xray:/etc/xray \
-p 443:443 \
--restart unless-stopped \
ghcr.io/xtls/xray-core:latest
单容器阶段这样没问题。但网关一旦多起来——核心代理一个容器、伪装站点一个容器、监控一个容器、日志清理一个容器——docker run 的劣势就暴露了:参数散落在 shell 历史里,启动顺序靠手动,改一个端口要先把整条命令找出来重敲一遍。而且裸容器之间没有依赖关系,你无法声明「先起 nginx 再起 xray」这种顺序。
Docker Compose 解决的就是这件事。一个 docker-compose.yml 文件把整个网关的所有服务、网络、卷、资源限制、健康检查全部声明出来:
- 声明式:整个网关的配置在一个文件里,git 可版本化,换机器复制过去就能起
- 依赖编排:depends_on 声明服务先后,condition 还能等健康检查通过再启动下游
- 一键管理:docker compose up -d 启动、docker compose ps 看状态、docker compose logs 看日志、docker compose down 整体下线
- 可回滚:配置变更前备份 compose 文件,出问题一条命令恢复
一句话:docker run 适合「试一下」,Compose 适合「长期跑」。自建网关是长期跑的东西,从第一天就用 Compose,后面省的事远超那几分钟的迁移成本。
二、网关目录设计与 Compose 骨架
推荐把整个网关放在一个独立目录里,方便备份和迁移:
/opt/proxy-gateway/
├── docker-compose.yml
├── .env # 端口、域名、凭据等环境变量
├── config/
│ ├── xray/
│ │ ├── config.json
│ │ └── cert/ # TLS 证书
│ └── nginx/
│ └── conf.d/
└── data/ # 持久化数据(如有)
核心的 docker-compose.yml 骨架如下(以 Xray 为例,sing-box 的写法完全类似):
services:
xray:
image: ghcr.io/xtls/xray-core:latest
container_name: xray
restart: unless-stopped
network_mode: host
volumes:
- ./config/xray:/etc/xray
environment:
- TZ=Asia/Shanghai
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD", "xray", "api", "statsquery", "--server=127.0.0.1:10085"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
几个关键点的选择逻辑:
- network_mode: host:代理服务走 host 网络最简单——端口直接暴露、UDP 性能好、不用折腾端口映射。缺点是没有容器网络隔离,但网关类服务本身就要监听公网端口,host 是主流选择。怕隔离问题的可以用 bridge + 显式 ports。
- restart: unless-stopped:容器崩了、VPS 重启了都会自动拉起。注意区分:always 在 Docker daemon 启动时无条件重启,unless-stopped 会尊重「手动 stop 过」的状态,运维时更不容易被系统拉起来捣乱。
- logging 限制:json-file 驱动默认无限增长,不限制的话日志能撑爆磁盘。max-size: 10m + max-file: 3 把单个容器日志锁死在 30MB 以内——这是自建 VPS 最常见的隐形杀手,见第六节。
- 镜像 tag 用 latest 还是固定版本:见第四节,这是自动更新的关键决策。
VPS 选型这里多说一句:Compose 编排本身对配置要求很低,1 核 512MB 的入门机器就能跑;但线路质量决定实际体验,买机器前建议先看这篇 2026 年 VPS 选购指南,搬瓦工、Vultr、RackNerd 的 CN2 GIA / 9929 线路差异讲得很细。
还有一个选型前就该想清楚的问题:自建网关的出口 IP 是机房 IP,流媒体解锁、跨境电商多账号这类对 IP 纯净度敏感的场景会天然受限。如果你主要就是干这个的,仙路湾 这类主打住宅 IP / 原生 IP 的机场比自建更省心——开箱即用,不用自己折腾 IP 质量。纯自建党可以跳过这段,继续往下看编排。
三、Compose 依赖编排:让整套服务按顺序健康启动
多容器网关(比如 Trojan + Nginx 反代 + acme.sh 证书)最烦人的问题是启动顺序。容器是并行启动的,如果 Nginx 先起来了、证书还没续好,反代就会报错。Compose 的 depends_on 可以控制:
services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./config/nginx:/etc/nginx/conf.d
- ./config/xray/cert:/etc/nginx/certs:ro
depends_on:
acme-sh:
condition: service_completed_successfully
acme-sh:
image: neilpang/acme.sh:latest
command: daemon
volumes:
- ./config/xray/cert:/acme.sh
environment:
- CF_Token=${CF_Token}
- CF_Zone_ID=${CF_Zone_ID}
restart: unless-stopped
condition: service_completed_successfully 表示 acme-sh 容器成功执行完(证书续期任务)之后 Nginx 才启动。配合 restart: unless-stopped,证书每 60 天自动检查续期,Nginx 始终加载最新证书,全程不用登录服务器。这套「证书自动续期 + 反代自动就绪」的思路,在 CDN 加速与 Cloudflare 实战指南 里有更完整的展开。
如果你的网关涉及多种协议共存(Shadowsocks + Trojan + Hysteria2),协议选择和落地的细节参考 主流代理协议横向对比 和前面的 Docker 部署全攻略,Compose 层只需要把每个协议各声明一个 service。
四、Watchtower 自动更新:镜像升级不再手动
自建网关最容易被忽视的维护项是「镜像升级」。Xray、sing-box 更新频率不低,安全修复和性能优化都在新版本里,但手动更新的流程是:ssh 登录 → docker compose pull → docker compose up -d,偶尔还要处理配置兼容问题。时间一长,多数人的镜像就停在几个月前了。
Watchtower 就是干这个的:它定期检查容器镜像是否有新版本,有就自动拉取并重建容器。在 Compose 里加一个服务:
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=Asia/Shanghai
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_SCHEDULE=0 0 4 * * *
- WATCHTOWER_LABEL_ENABLE=true
几个配置要点:
- WATCHTOWER_SCHEDULE:cron 表达式,上面是每天凌晨 4 点检查一次。避开晚高峰,省得更新瞬间影响正在用的人。
- WATCHTOWER_LABEL_ENABLE=true + 容器加 com.centurylinklabs.watchtower.enable=true 标签:只更新你明确标记的容器。强烈建议开这个——否则 Watchtower 会把机器上所有容器(包括它自己、包括你随手起的其他服务)都更新一遍,不可控。
- WATCHTOWER_CLEANUP=true:更新后自动删除旧镜像,防止磁盘被历史镜像占满。
- 只对网关容器开启:在 xray、nginx 等服务的定义里加一行 labels: - "com.centurylinklabs.watchtower.enable=true",其他容器不标,Watchtower 就不会碰。
给容器加标签的完整写法:
services:
xray:
image: ghcr.io/xtls/xray-core:latest
labels:
- "com.centurylinklabs.watchtower.enable=true"
# ... 其余配置同前
自动更新的两个大坑,提前说清楚:
第一个坑是镜像 tag 用 latest。latest 是移动靶,新版本可能改配置格式(比如 Xray 大版本升级时 config.json 结构变化),Watchtower 拉下来重建后容器起不来,网关就断了。稳妥做法有两种:一是镜像 tag 固定到具体版本(如 xray-core:v25.x.x),Watchtower 只会在你手动改 tag 时才更新——但这等于放弃自动更新;二是保持 latest,接受「偶尔更新失败」的小概率,靠第四节的健康检查 + autoheal 自动兜底,更新失败容器会处于 unhealthy 状态被重启或回滚。对网关这种「挂了能快速恢复」的服务,我倾向后者——毕竟安全补丁跟不上的风险,比偶发更新失败大得多。
第二个坑是「更新即重启」的时机。默认 Watchtower 发现新版本立刻重建容器,正在跑的关键连接会瞬间断开。用 WATCHTOWER_SCHEDULE 把检查时间放到凌晨低峰期,影响最小。如果追求更平滑,可以加 --rolling-restart(逐个容器滚动重启),但网关类服务一般只有一个核心容器,直接重建问题不大。
五、健康检查 + autoheal:容器挂了自动恢复
restart: unless-stopped 只能处理「容器进程崩溃退出」的情况。更常见也更隐蔽的问题是:容器进程还活着,但服务已经坏了——比如 xray 进程僵死、端口被占用、配置热加载失败。这时候 restart 策略不会触发,你的网关「看起来在跑,实际上连不通」。
healthcheck 就是给容器装一个「体检器」,定期探测服务是否真的健康:
healthcheck:
test: ["CMD", "xray", "api", "statsquery", "--server=127.0.0.1:10085"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
参数含义:每 30 秒执行一次探测命令,5 秒超时,连续 3 次失败标记为 unhealthy,启动后给 10 秒宽限期(避免刚启动还没就绪就被误判)。探测命令按服务写:Xray 用 api statsquery,Nginx 用 wget 或 curl 探测本地端口,sing-box 可以用 sing-box check 校验配置后配合端口探测。
光有 healthcheck 还不够——它只是「报告」状态,不会「修复」。autoheal 容器负责第二步:监听 Docker 事件,发现 unhealthy 容器就自动重启它。加一个服务:
autoheal:
image: willfarrell/autoheal:latest
container_name: autoheal
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=15
组合效果:xray 进程僵死 → healthcheck 30 秒内发现 → 容器变 unhealthy → autoheal 15 秒内重启容器 → restart 策略兜底拉起。整个过程全自动,最长不到一分钟,用户几乎无感知。这是 2026 年自建网关「免维护」体验的核心三件套:Compose 声明 + Watchtower 更新 + healthcheck/autoheal 自愈。
自愈只能解决「进程级」故障。线路拥堵、晚高峰丢包这种「网络级」问题,自动化帮不上忙——该上 BBR 的上 BBR(参考 晚高峰变慢完全指南:TCP 拥塞控制与 BBR),该换线路的换线路。如果你的主要痛点是晚高峰速度而不是维护,可能根本不需要自建——自由猫 的 IEPL 专线(¥9/月起步)延迟表现接近自建 CN2 GIA,晚高峰稳定性反而更好,因为机场有大量节点可以自动切换。维护自动化解决「服务活着」,线路质量解决「速度快」,两件事别混为一谈。
六、日志与磁盘保护:防止隐形爆盘
自建 VPS 最常见的故障不是服务挂了,而是磁盘满了。日志、镜像、容器层文件会悄悄吃掉空间,一旦 / 分区 100%,Docker daemon 直接罢工,所有容器一起挂。防护分三层:
第一层是 Compose logging 限制(第二节已写):每个容器日志锁死在 30MB。第二层是 Watchtower 的 CLEANUP=true:更新后自动清理旧镜像。第三层是定时清理悬空资源:
# crontab 每周日凌晨 3 点清理
0 3 * * 0 docker system prune -af --volumes --filter "until=168h"
--filter "until=168h" 只删 7 天前的悬空镜像,保留最近可能回滚用的版本。想更优雅的话,可以把清理也容器化:
docker-cleanup:
image: docker:cli
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
entrypoint: /bin/sh
command: -c "while true; do docker system prune -af --filter 'until=168h' >/dev/null 2>&1; sleep 86400; done"
再配合一个简单的磁盘告警(比如 crontab 里 df -h 超过 80% 发一封邮件或推送到 Telegram),磁盘问题基本就能在变成事故之前被发现。更完整的监控方案(cAdvisor 容器指标、流量统计、多节点热备)参考 自建 VPN 进阶管理:多节点热备、流量监控与成本优化。
七、故障自愈实战演练:验证你的网关真的「免维护」
配置完不等于万事大吉——建议搭好后做一轮破坏性演练,确认自动化真的在工作:
- kill 容器测试:docker kill xray,观察 5-10 秒内 restart 策略自动拉起,docker compose ps 显示 Up。
- 僵死测试:手动把 xray 进程 kill -STOP 挂起(模拟僵死),healthcheck 探测失败,30-60 秒内 autoheal 重启容器,恢复 Up (healthy)。
- 更新测试:手动 docker compose pull 触发一次版本变化,确认 Watchtower 能正常重建、新版本配置兼容。
- 重启测试:sudo reboot 重启 VPS,确认开机后所有容器按 depends_on 顺序自动就绪,无需人工干预。
- 回滚测试:如果某次自动更新后容器起不来(unhealthy 循环),手动 docker compose up -d --force-recreate 指定旧 tag 回滚,确认 compose 文件里改一行 tag 就能恢复。
演练全部通过,你的网关才算是真正进入了「免维护」状态。以后每周登录一次看 docker compose ps 全是 healthy,就够了。
八、自动化之后的真实成本账:自建 vs 机场
把自动更新和自愈都配好之后,自建网关的维护成本已经压到很低——每周最多 10 分钟看一眼状态。但成本账还要算全:VPS 月租(¥30-100)、线路质量波动、单点故障风险(VPS 被墙就全断)、以及「自建 IP 是机房 IP」这个绕不开的限制——流媒体解锁、跨境电商多账号这类对 IP 纯净度敏感的场景,机房 IP 天然吃亏。
如果你的核心需求是流媒体解锁、跨境电商多账号运营这种对 IP 质量要求高的场景,自建网关不一定是最优解——主打住宅 IP / 原生 IP 的机场就是为这类场景设计的,开箱即用,不用自己折腾 IP 纯净度(第二节提过这类选择)。
至于纯速度党:自建 + 好线路 VPS 的天花板确实高,但普通用户更常见的情况是「花了半天调优,晚高峰还是堵」。这种时候,把时间花在选一条好线路上,比继续折腾自建划算得多。
做最终决策前,把两边的账摊开看:
| 维度 | 自建网关(Compose 自动化后) | 机场订阅 |
|---|---|---|
| 月度成本 | ¥30-100(VPS) | ¥9-50 |
| 维护投入 | 每周约 10 分钟看状态 | 零维护 |
| 延迟上限 | 看 VPS 线路,CN2 GIA 可到 50-80ms | IEPL 专线 50-100ms |
| IP 纯净度 | 机房 IP,流媒体/多账号受限 | 可选住宅 IP / 原生 IP |
| 故障风险 | 单点,VPS 被墙全断 | 50+ 节点自动切换 |
| 可玩性 | 高,完全可控 | 低,即开即用 |
我的建议很直接:喜欢折腾、追求极致可控、有技术底子的,Compose 自动化这套值得搭,本文所有配置抄下来半小时就能跑通;不想维护、要稳定省心的,直接买机场——万达云 的中转 + IEPL 混合线路(¥16.8/月起步)覆盖多设备场景,把省下来的时间花在正事上,是 2026 年大多数人的理性选择。两者也不是非此即彼,机场 + 自建 VPS 混合架构 用机场做入口、VPS 做落地,兼顾稳定与可控,适合进阶玩家。