如果你已经有 Claude、OpenAI 或 Gemini 账号,想在 Codex、Cherry Studio 等客户端里使用,可以在自己的 Ubuntu 服务器上部署 Sub2API。账号保存在网关后台,客户端只填写网关地址和单独生成的 API Key,不需要拿到原账号的登录信息。

页面底部的下载区提供了一套面向空白 Ubuntu 24.04 服务器的 Sub2API 主机部署脚本包。它从管理电脑通过 SSH 把脚本传到服务器,再依次配置 Ubuntu、Docker、Sub2API、PostgreSQL、Redis 和 Caddy。

选择部署方式并准备 Ubuntu 服务器

先根据服务器当前的环境选择部署方式:

服务器现状使用方式
空白的 Ubuntu 24.04 独立服务器运行完整脚本,由脚本安装 Docker、部署 Sub2API 并配置 Caddy
已经安装 Docker,80/443 尚未被其他服务使用添加 --skip-system,跳过 Ubuntu 配置和 Docker 安装
已经运行 Nginx、Caddy 或其他网站使用官方 Docker Compose 部署,再把 Sub2API 接入现有反向代理;不要让完整脚本接管 80/443

使用官方 Docker Compose 时,跳过后面的主机脚本命令;域名能够通过 HTTPS 打开后,从“登录后台并修改管理员账号”继续。

完整脚本的执行顺序

完整脚本从管理电脑发起。它先检查参数和 SSH 连接,再把 provisioninghost-gatewaysub2api 三个目录同步到服务器;后续的系统配置、Compose 文件、Caddy 入口和部署检查都在服务器上执行。

管理电脑校验参数并通过 SSH 同步脚本后,服务器依次识别现有环境、配置 Ubuntu 与 Docker、生成 Sub2API 配置和数据目录、配置 Caddy 并启动容器,最后检查 HTTPS、端口和 PostgreSQL 数据目录
只有 Ubuntu 与 Docker 配置阶段会被 --skip-system 跳过;云服务器、安全组和 DNS 记录仍需在云控制台中准备。

“系统加固”会修改哪些设置

这里的“系统加固”指脚本会实际修改的 Ubuntu 设置:启用 UTC 与 NTP,安装基础运维包,关闭 SSH 的 root 登录和密码登录,只允许指定运维用户使用密钥登录,并配置 fail2ban、自动安全更新、UFW 和 8 GB swap。UFW 放行 SSH、80 和 443;Docker 的配置还会启用日志轮转,并把 journald 的最大占用设为 500 MB。

添加 --skip-system 后,脚本不会执行这些 Ubuntu 设置,也不会安装或重新配置 Docker。它仍会确认运维用户、生成或复用环境密钥、写入 Compose、建立持久化目录、配置 Caddy、启动三个容器并运行部署检查。因此,这个参数只适合已经按自己的方式完成系统配置和 Docker 安装、并且 80/443 没有被其他服务占用的服务器。

完整脚本只适配 Ubuntu 24.04,因为其中包含 Ubuntu 的软件源、SSH、UFW 和 Docker 安装配置。服务器还需要 sudo 权限、可用的公网 IPv4、一个已经解析到该地址的域名,以及能够通过 SSH 登录的端口。

个人或小团队可以先准备 2 核 4 GB 的服务器。磁盘空间要同时容纳 Sub2API、PostgreSQL、Redis、容器日志、Docker 镜像和一份备份:

所需空间 ≈ Sub2API 数据 + PostgreSQL 数据 + Redis 数据
         + 容器日志与 Docker 镜像 + 一份备份
         + 30% 余量

域名示例使用 sub2api.example.com。添加 A 记录后,确认解析结果与服务器公网地址一致:

dig +short sub2api.example.com A
curl -4 https://ifconfig.me

云安全组和主机防火墙都要允许 80/443;8080 不应加入公网规则。

端口允许来源用途
22/tcp管理网络或固定来源SSH 管理
80/tcp公网ACME HTTP 验证与 HTTPS 跳转
443/tcp公网管理后台与 API 请求
8080/tcp127.0.0.1Caddy 到 Sub2API 的回环连接
5432、6379不发布到宿主机PostgreSQL 与 Redis 的容器内连接

下载脚本并部署 Sub2API

选择完整脚本或 --skip-system 时,下载下面的脚本包。服务器已经有 Nginx、Caddy 或其他网站时,改用官方的 Docker Compose 部署(在新标签页打开),并沿用现有 HTTPS 入口,不执行这个主机脚本包。

先核对下载结果:

shasum -a 256 sub2api-deploy.tar.gz
# Linux 使用:sha256sum sub2api-deploy.tar.gz
tar -tzf sub2api-deploy.tar.gz | less

解包后保持 provisioning/host-gateway/sub2api/ 三个目录的相对位置不变:

tar -xzf sub2api-deploy.tar.gz
cd sub2api-deploy
export SERVER_IP="203.0.113.10"
export SSH_KEY_PATH="$HOME/.ssh/id_ed25519"

bash deployments/scripts/sub2api/provision-sub2api-host.sh \
  --host "${SERVER_IP}" \
  --domain sub2api.example.com \
  --ssh-user ubuntu \
  --ssh-key "${SSH_KEY_PATH}" \
  --dry-run

--dry-run 只显示参数、连接目标和即将执行的阶段,不同步文件,也不修改远端。输出中的主机、域名、SSH 用户和镜像都正确后,去掉 --dry-run 正式执行:

bash deployments/scripts/sub2api/provision-sub2api-host.sh \
  --host "${SERVER_IP}" \
  --domain sub2api.example.com \
  --ssh-user ubuntu \
  --ssh-key "${SSH_KEY_PATH}"

脚本固定使用 weishaw/sub2api:0.2.4。这是脚本包的验证版本,不代表当前最新版本;Sub2API Releases(在新标签页打开) 出现新版本时,也不会在重复部署中自动替换正在运行的镜像。

这些参数会改变部署行为:

参数作用
--sub2api-image显式指定要部署的镜像版本
--deploy-user指定负责 sudo 与 Docker 运维的用户
--skip-system主机已按自己的方式完成 Ubuntu 配置并安装 Docker 时,跳过这两个阶段
--skip-start只写入配置,不启动容器和入口
--skip-dns-check暂时不检查域名解析;不会替你创建 DNS 记录

脚本不调用云厂商 API。购买实例、开放安全组和添加 DNS 记录仍要在对应控制台完成。

检查 HTTPS 和服务状态

脚本把运行文件与数据分开保存。环境文件已经存在时,不会重新生成 PostgreSQL 密码、JWT_SECRETTOTP_ENCRYPTION_KEY

/opt/sub2api/
├── compose.yaml
├── data/
├── postgres_data/
└── redis_data/

/etc/sub2api/
├── sub2api.env
├── bootstrap-credentials.txt
└── managed-install

已有容器或数据目录不符合这套路径时,脚本会拒绝接管。先确认旧实例使用的镜像、挂载目录和数据库版本,再单独迁移;不要让新脚本猜测旧数据库密码或把未知数据卷当成可删除数据。

正式部署结束时会自动执行检查。需要重新运行时,在管理电脑上执行:

ssh -i "${SSH_KEY_PATH}" ubuntu@"${SERVER_IP}" \
  'sudo bash /tmp/sub2api-provisioning/sub2api/verify-sub2api-host.sh \
    --domain sub2api.example.com \
    --acme-email admin@example.com'

如果部署时通过 --remote-dir 改过远端脚本目录,把命令中的 /tmp/sub2api-provisioning 换成实际路径。

命令会逐项检查以下状态:

  • Caddy 正在监听 80/443,HTTPS /health 返回成功;
  • docker port sub2api 8080/tcp 只返回 127.0.0.1:8080
  • PostgreSQL 与 Redis 没有宿主机端口;
  • PGDATA、运行中的 SHOW data_directory 和宿主机 bind mount 指向同一份 PostgreSQL 数据;
  • Redis PING 返回 PONG,三个容器的健康状态正常。

公网请求先到 Caddy 的 80/443,再由 Caddy 转给 127.0.0.1:8080。PostgreSQL 和 Redis 只在 Compose 私有网络中使用 5432 和 6379,不需要开放宿主机端口;Sub2API 访问 AI 服务时再通过出站 443 建立连接。

客户端和管理员浏览器经 Caddy HTTPS 入口访问回环地址上的 Sub2API;Sub2API 在私有网络中访问 PostgreSQL 与 Redis,并向外部 AI 平台发起请求
公网只开放 80 和 443;8080 只监听 127.0.0.1,5432 和 6379 不发布到宿主机。

PGDATA 是 PostgreSQL 的启动声明,SHOW data_directory 是运行实例实际打开的位置,bind mount 则说明数据写到宿主机的哪个目录。三处必须指向同一份数据;只看容器显示 running 或只看挂载列表,都可能把空目录误认成正在使用的数据库。

这是一套单机部署。服务器、磁盘或 Caddy 停止工作时,整个入口都会中断。需要多实例时,PostgreSQL、Redis、会话状态和账号调度都要重新设计,不能直接复制第二套 Compose。

登录后台并修改管理员账号

初始管理员邮箱和密码保存在仅 root 可读的文件中:

sudo cat /etc/sub2api/bootstrap-credentials.txt

浏览器打开 https://sub2api.example.com。页面进入登录界面且证书有效,说明 DNS、Caddy 与本机 8080 端口之间已经接通。

首次登录后立即更换管理员密码,把新密码保存到密码管理器,再启用两步验证。恢复码只保存在离线位置,不要和服务器上的环境文件放在一起。

两个环境变量必须长期保持稳定:

变量用途被替换后的结果
JWT_SECRET签发登录会话现有会话全部失效,所有用户需要重新登录
TOTP_ENCRYPTION_KEY加密两步验证密钥已绑定的动态口令无法解密,用户无法完成 2FA 登录

日常重复部署不会覆盖这两个值。确实需要轮换 TOTP_ENCRYPTION_KEY 时,先解除或重建所有用户的两步验证,再替换密钥;顺序颠倒会直接锁住账号。

添加 AI 账号并测试连接

打开左侧的“账号管理”,点击右上角的“添加账号”。

Sub2API 账号管理页,左侧导航选中账号管理,右上角显示添加账号按钮

在弹窗中填写一个便于识别的账号名称,再选择平台和账号类型。页面提供 OAuth 和 API Key 两种接入方式:账号支持 OAuth 时,选择 OAuth 并按“下一步”完成授权;使用平台 API Key 时,选择“API Key”并填写对应凭据。模型白名单和模型映射不是必填项,只在需要限定模型或替换模型名时设置。

Sub2API 添加账号弹窗,可填写账号名称,选择 OpenAI 等平台、OAuth 或 API Key 账号类型,并可选择模型白名单或模型映射

账号保存后会回到账号管理列表。列表会显示平台、OAuth 或 API Key 类型、账号状态和已加入的分组;账号状态显示“正常”后,再把它加入分组。

创建分组并生成 API Key

账号提供可调用的模型,分组决定这些账号怎样对外使用,API Key 则把指定分组交给一台设备或一个程序。

AI 平台账号先加入分组,分组限定模型与策略,再由 Sub2API API Key 把这组能力交给具体设备或用户
一个账号可以加入多个分组;每把 API Key 对应一个设备、用户或程序,停用时不会影响其他 Key。

创建分组并加入账号

打开左侧的“分组管理”,点击右上角的“创建分组”。

Sub2API 分组管理页,左侧导航选中分组管理,右上角显示创建分组按钮

填写分组名称,选择与账号一致的平台。费率倍数决定用量按什么倍率计算;每分钟请求数填 0 时不限制,其他数值表示该分组每分钟允许的最大请求数。推理强度上限会限制这个分组可以使用的最高推理级别。

Sub2API 创建分组弹窗,可填写名称、描述、平台、费率倍数、每分钟请求数和推理强度上限

创建完成后,在分组列表的操作列点击“编辑”,把刚才状态正常的账号加入这个分组。账号数一列会显示可用、限流和总账号数;确认总数不再为 0 后,再为这个分组生成 API Key。

为客户端生成 API Key

打开左侧“我的账户”下的“API 密钥”,点击右上角的“创建密钥”。名称直接写用途,例如 macbook-codexteam-ci,再选择刚才创建的分组。

Sub2API API 密钥页,右上角显示创建密钥按钮,列表显示密钥名称、所属分组、用量和状态

密钥生成后,列表会显示所属分组、并发数、用量和状态。把 Key 复制到对应客户端,不要写入 Git 仓库或公开截图。

使用 API Key 发起第一次请求

在 API 密钥列表的操作列点击“使用密钥”,Sub2API 会按 Codex CLI、Codex CLI WebSocket 或 OpenCode 生成配置。选择 macOS / Linux 或 Windows,再复制界面中的配置。

Sub2API 使用 API 密钥弹窗,可选择 Codex CLI、WebSocket 或 OpenCode,再按 macOS Linux 或 Windows 复制配置
弹窗会使用当前 Sub2API 实例的域名生成配置。

其他兼容 OpenAI 接口的客户端需要填写三项内容:

客户端字段填写内容
API 地址或 Base URLhttps://sub2api.example.com/v1
API Key后台刚生成的 Sub2API API Key
模型当前分组已经开放的模型 ID

不同客户端的字段名称可能不同,但 API 地址都应指向你的 Sub2API 域名,而不是 OpenAI、Anthropic 或 Google 的官方地址。模型 ID 先从网关查询,不要照抄其他账号或旧配置。

先查询当前 Key 能看到的模型,避免把后台名称或旧配置直接写进请求:

export SUB2API_BASE_URL="https://sub2api.example.com/v1"
export SUB2API_API_KEY="<刚创建的 API Key>"

curl -fsS "${SUB2API_BASE_URL}/models" \
  -H "Authorization: Bearer ${SUB2API_API_KEY}"

从返回结果中选一个分组允许的模型,再发起最短请求。不同 AI 平台支持的接口可能不同;下面使用 OpenAI Responses 路径:

export SUB2API_MODEL="<模型 ID>"

curl -fsS "${SUB2API_BASE_URL}/responses" \
  -H "Authorization: Bearer ${SUB2API_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"${SUB2API_MODEL}"'",
    "input": "Reply with: gateway ready"
  }'

请求返回后,打开后台的用量记录,找到同一时间的 Key、模型和 Token 消耗。请求成功但没有对应记录时,先确认调用地址是网关域名,再检查 Key 所属分组和当前版本的计费设置。

检查错误 Key、未开放模型和停用状态

把 API Key 改成一个不存在的值,再请求一次 /models。Sub2API 应拒绝这次请求,后台也不应生成正常用量;如果仍能列出模型,客户端可能没有访问当前网关,或者域名前还有另一层认证代理。

换回有效 Key,再把模型改成当前分组没有开放的值。请求应直接失败,不能自动换成另一个模型。错误码和正文会随 Sub2API 版本、接口协议与账号类型变化,但未开放的模型不应返回正常内容,也不应产生一次正常用量。

最后停用测试 Key,再重放刚才成功的请求。停用后请求应被拒绝,不需要更换后台保存的 AI 账号。正式 Key 可以重新启用,测试 Key 则保持停用。

正式 Key 随后可以填进 Codex、Cherry Studio 或其他客户端。客户端只保存 Sub2API 的 Base URL 和 API Key,不需要保存后台账号的 OAuth 凭据或 AI 平台 API Key。

备份和升级 Sub2API

备份 Sub2API 数据和配置

备份前先停止 Sub2API、PostgreSQL 和 Redis,避免归档过程中数据继续写入。归档需要同时包含数据库、环境变量、Compose 配置和 Caddy 配置:

cd /opt/sub2api

sudo docker compose \
  --env-file /etc/sub2api/sub2api.env \
  -f compose.yaml stop sub2api postgres redis

backup="/var/backups/sub2api/sub2api-$(date -u +%Y%m%dT%H%M%SZ).tar.gz"
sudo install -d -m 700 /var/backups/sub2api
sudo tar -C / -czf "${backup}" \
  opt/sub2api \
  etc/sub2api \
  etc/caddy \
  var/lib/caddy
sudo sha256sum "${backup}" | sudo tee "${backup}.sha256"

sudo docker compose \
  --env-file /etc/sub2api/sub2api.env \
  -f compose.yaml start postgres redis sub2api

curl --fail --retry 30 --retry-delay 2 --retry-all-errors \
  https://sub2api.example.com/health

.tar.gz.sha256 一起复制到另一台主机或对象存储。两者只留在同一块系统盘时,系统盘损坏会同时丢失服务和备份。

使用固定版本升级 Sub2API

升级前查看目标版本的 Release Notes,确认数据库迁移和配置变化,并先运行上面的备份命令。然后把目标版本显式传给脚本:

export TARGET_VERSION="0.2.5" # 示例;执行前以 Releases 页面为准

bash deployments/scripts/sub2api/provision-sub2api-host.sh \
  --host "${SERVER_IP}" \
  --domain sub2api.example.com \
  --ssh-user ubuntu \
  --ssh-key "${SSH_KEY_PATH}" \
  --sub2api-image "weishaw/sub2api:${TARGET_VERSION}"

不要用可变标签执行升级。新版本修改数据库后,只把应用镜像改回旧版本不会恢复旧数据;需要回退时,应使用升级前的备份。

Sub2API 无法访问或请求失败时怎么排查

后台打不开、Caddy 返回 502、API 返回 401、模型列表为空和请求超时,分别对应不同的检查位置:

症状首先检查处理方法
HTTPS 无法打开DNS、80/443、安全组、caddy.service修正解析或放行规则,查看 journalctl -u caddy
Caddy 返回 502127.0.0.1:8080/health、Sub2API 日志根据日志修正应用配置,再重启 Sub2API
后台可登录,API 返回 401Authorization 头、Key 状态、所属分组重新复制正确 Key,启用 Key 或修正分组
/models 为空或缺模型账号状态、分组平台和模型范围确认账号状态正常,再调整分组
请求超时AI 平台连通性、代理、账号可调度状态从服务器直接测试 AI 平台域名,修正代理或暂停异常账号

先在服务器执行这些命令,判断问题停在 Caddy、Sub2API 还是容器:

cd /opt/sub2api
sudo docker compose \
  --env-file /etc/sub2api/sub2api.env \
  -f compose.yaml ps
sudo docker compose \
  --env-file /etc/sub2api/sub2api.env \
  -f compose.yaml logs --tail 200 postgres redis sub2api
docker port sub2api 8080/tcp
curl -fsS http://127.0.0.1:8080/health
curl -fsS https://sub2api.example.com/health
sudo systemctl status caddy --no-pager

curl http://127.0.0.1:8080/health 失败时,先看 Sub2API 日志;本机健康但公网失败时,检查 Caddy、DNS 和 80/443;两处都正常而客户端仍然失败时,回到 API Key、分组和账号页面检查状态。