用 Docker 部署 sub2api 并配 Caddy 反代
记录 sub2api 和 Caddy 在 Docker 里的部署过程,sub2api 用官方一键脚本起,Caddy 用 host 网络模式反代。
前置条件
- 一台 Linux 服务器(Docker 20.10+,支持
host-gateway) - 一个 A 记录指向服务器的域名(如果有 AAAA 记录,也要指向这台服务器)
- 腾讯云轻量云防火墙放行
22、80、443(HTTP/3 还需要放行443/UDP)
第一步:部署 sub2api
sub2api 官方提供了一键准备脚本,会下载 compose 文件、生成密码、建数据目录:
mkdir -p ~/sub2api && cd ~/sub2api
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
这是直接执行 main 分支脚本,适合自己临时部署;要长期复现,建议固定到某个 release 或 commit。
脚本做的事:
- 下载
docker-compose.yml(app + PostgreSQL + Redis 三件套) - 生成
POSTGRES_PASSWORD、JWT_SECRET、TOTP_ENCRYPTION_KEY三个密码写入.env - 创建
data/、postgres_data/、redis_data/数据目录
脚本只是「准备」,不会启动容器。.env 里真正硬性必填的只有 POSTGRES_PASSWORD(compose 语法层面要求),其他变量都有合理默认值。手动改两个值就够了:
nano .env
# ADMIN_EMAIL=你的邮箱@example.com
# ADMIN_PASSWORD=设一个强密码
不设也行,密码会在首次启动日志里生成。最后启动:
docker compose up -d
docker compose logs -f sub2api # 确认没有启动或数据库连接错误
这一步结束后,sub2api 会在宿主机的 0.0.0.0:8080 监听。腾讯云轻量云防火墙是白名单模式、没有放行 8080,因此公网访问不到;但宿主机上的其他进程仍然可以访问。这里保持官方 Compose 不改,公网隔离依靠腾讯云防火墙。
第二步:部署 Caddy
Caddy 单独起一个容器,用 host 网络模式直接共享宿主机网络栈。这样 Caddyfile 里 reverse_proxy localhost:8080 就能命中 sub2api,不用记 Docker 内部网络名。
建目录,准备两个文件:
mkdir -p ~/caddy && cd ~/caddy
nano Caddyfile
Caddyfile 最简版三行就够:
sub.example.com {
reverse_proxy localhost:8080
}
把 sub.example.com 换成你的域名。Caddy 默认行为已经包含:
- 自动向 Let’s Encrypt 申请证书
- 自动续期
- HTTP 自动跳转 HTTPS
- 启用 HTTP/2、HTTP/3(客户端支持且
443/UDP已放行时) - 默认只启用 TLS 1.2 和 1.3(TLS 1.0/1.1 默认就禁用了,不用手动配)
想要更多优化可以扩展,但起步阶段三行就够了。
再写一个 docker-compose.yml,避免每次升级都要敲一长串 docker run:
nano docker-compose.yml
services:
caddy:
image: caddy:2-alpine
container_name: caddy
network_mode: host
restart: unless-stopped
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
启动:
docker compose up -d
docker compose logs -f caddy # 确认没有 ACME 或端口占用错误
caddy_data 卷里存着证书和私钥,重启容器不会丢;caddy_config 卷存运行时配置缓存。
caddy:2-alpine 是滚动标签,升级前最好先备份并查看 release notes。
第三步:验证
curl -fsS https://sub.example.com/health
# 能正常返回且命令退出码为 0 就行
浏览器打开 https://sub.example.com,用配置的 admin 账号登录。
拓扑总览
部署完之后整个机器长这样:
公网
│
┌─────────┴─────────┐
│ 云防火墙: 80/443 │
└─────────┬─────────┘
│
┌─────────────┴─────────────┐
│ Caddy 容器 (host network) │
│ 反代 localhost:8080 │
└─────────────┬─────────────┘
│ localhost 回环
┌─────────────┴─────────────┐
│ sub2api (0.0.0.0:8080) │
│ ├─ PostgreSQL (内部网络) │
│ └─ Redis (内部网络) │
└───────────────────────────┘
Caddy 和 sub2api 通过 localhost 回环连起来。
常用维护命令
sub2api 那边(~/sub2api 目录):
docker compose ps # 看状态
docker compose logs -f sub2api # 看日志
docker compose restart # 重启
docker compose pull && docker compose up -d # 升级
Caddy 那边(~/caddy 目录):
docker compose ps # 看状态
docker compose logs -f caddy # 看日志
docker compose restart # 重启
docker compose pull && docker compose up -d # 升级
docker exec caddy caddy reload --config /etc/caddy/Caddyfile # 改完 Caddyfile 热重载,不中断
升级前先做备份;pull 会跟随镜像标签变化,不要在不了解版本变更时直接跨大版本升级。
加新服务时怎么扩展
将来再加一个服务(比如 Minecraft 服务器),步骤一样:
- 按同样套路建一个独立目录跑新服务,让它监听某个端口
- 在
~/caddy/Caddyfile里追加一个域名块:
mc.example.com {
reverse_proxy localhost:25565
}
- 热重载 Caddy:
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
Caddy 自动为新域名申请证书,等 DNS 生效且后端正常监听后即可访问。
备份策略
这是一份停机后的冷备份。按当前脚本生成的 Compose,sub2api 数据都在 ~/sub2api,备份就是打包:
cd ~
docker compose -f sub2api/docker-compose.yml down
tar czf sub2api-backup-$(date +%F).tar.gz sub2api/
docker compose -f sub2api/docker-compose.yml up -d
.env 里的三个密码也要跟着一起备份:POSTGRES_PASSWORD 要和现有数据库匹配;换 JWT_SECRET 会让已有会话失效;丢失 TOTP_ENCRYPTION_KEY 会影响已启用的 2FA。恢复前先在临时目录演练一次,不要把“能解压”当成“能恢复”。Caddy 还要单独备份 ~/caddy/Caddyfile 和 caddy_data 卷,否则恢复后需要重新申请证书。
一些踩过的坑
坑一:把脚本直接在 ~/ 跑。 准备脚本会把文件铺在当前目录,一定要先 mkdir 子目录再 cd 进去执行,不然家目录被一堆 data/、postgres_data/ 污染。
坑二:用 docker-compose.local.yml 当文件名执行。 准备脚本下载时会自动把这个文件重命名为 docker-compose.yml,所以直接 docker compose up -d,不用 -f 参数。
坑三:Caddy 容器加不进 sub2api 的内网。 既然 Caddy 用 host 网络模式,就不用管 Docker 内部网络名;这个方案面向 Linux,Caddyfile 反代地址保持 localhost:8080。