| name | sub2api-relay-setup |
| description | 从零搭建一个自己可控的 AI API 中转站并部署 Sub2API(Wei-Shaw/sub2api)网关。指导完成:去哪买服务器与买什么规格、 域名合规选择、系统初始化(Docker/Caddy/fail2ban/UFW/swap)、docker compose 部署、Caddy 反代+域名+Let's Encrypt 自动 HTTPS、 后台 settings 表与数据库配置、管理员与普通用户/API Key 治理、上游渠道与分组接入、把旧 CRS 服务器迁移成 Sub2API、 烟测与长期运维(升级/备份/1C2G 防 OOM/欠费恢复)。所有真实 IP、域名、密码一律用占位符。 Use when the user wants to: 搭建/自建 AI 中转站、部署 sub2api、搭 AI API 网关、订阅转 API、把 CRS 迁到 sub2api、 给中转站换域名、配 Caddy 反代 HTTPS,或问"怎么买服务器搭中转"。触发词:中转站 / sub2api / AI API 网关 / 自建中转 / relay / gateway / 订阅转 api / Caddy HTTPS / 换域名 / CRS 迁移。
|
从零搭一个自己的 AI 中转站(Sub2API)
帮别人(或自己)拥有一个自己可控的 AI API 网关:不再依赖别人代搭的中转站,买一台服务器,部署
Wei-Shaw/sub2api,绑自己的域名,发 API Key 给自己/团队/客户用。
Sub2API 是什么:一个 "AI API Gateway Platform for Subscription Quota Distribution"。管理员在后台维护上游账号 / key,
下游用户拿平台发的 API Key 调模型,平台负责鉴权、计费、负载均衡、请求转发,内置 sticky session。它覆盖的是
"多模型 API 网关 / key 分发 / 额度管理"这一层。
使用约定(先读)
-
所有 IP、域名、密码、SSH key 名都是占位符,照抄前替换:
| 占位符 | 含义 | 示例 |
|---|
<SERVER_IP> | 中转服务器公网 IP | 203.0.113.10 |
<DOMAIN> | 对外中转域名(未备案 .com) | example-gw.com |
<API_HOST> | API 子域名 | api.<DOMAIN> |
<SSH_KEY> | 本地 SSH 私钥路径 | ~/.ssh/id_ed25519 |
<APP_DIR> | Sub2API 应用目录 | /opt/ai-gateway/apps/sub2api |
<ADMIN_EMAIL> | 管理员登录邮箱 | admin@example.com |
-
真实密码 / API key 永不写进任何文档、git 仓库或聊天。只存服务器 root-only 文件
/opt/ai-gateway/secrets/*.env(权限 600)或本地 .env。
-
每步做完先跑对应验证命令再进下一步;不靠"看起来成功了"。
阶段总览
0. 选型确认 → 确认要的是"多模型 API 网关"这层(Sub2API),不是 Claude OAuth 池(那是 CRS)
1. 买服务器 → 1C2G / Ubuntu LTS + 未备案 .com 域名 —— 详见 references/buy-server.md
2. 系统初始化 → Docker + Caddy + fail2ban + UFW + swap + 目录
3. 部署 Sub2API → docker compose 三容器,只绑 127.0.0.1:8080
4. 域名+HTTPS → DNS A 记录 → Caddy 反代 → Let's Encrypt 自动签证书
5. 后台配置 → settings 表改 3 个 URL + 注册策略 + SMTP
6. 账号治理 → 管理员(数据库设 role)+ 普通用户 / API Key 分发
7. 上游渠道 → 接上游账号/key + 分组,让 key 真能调模型
8. 烟测 → /health、/v1/models(401 是对的)、真实 prompt
9. 运维 → 升级、备份、防 OOM、欠费恢复
配套参考文件(按需读,别一次全读):
0. 选型确认
别用一个系统硬吃所有需求:
- 多模型 API 网关 / key 分发 / 额度管理 → 就是 Sub2API(本 skill)。"我要给自己/团队/客户发 API key,后台管渠道和额度"。
- Claude Code / Claude OAuth 订阅池 → 是
claude-relay-service(CRS),不同项目。已有 CRS 机器想换成 Sub2API 见第 8b 节。
1. 买服务器
一句话:一台 1C2G / Ubuntu 24.04 LTS 的境外/香港小机 + 一个未备案的 .com 域名,摸索期约 $12/月。
去哪买、买哪个规格、账号怎么开、付款卡在哪、域名怎么选——读 references/buy-server.md,里面有具体厂商、下单核对项和踩坑。
域名合规红线(务必记住):中转域名必须未备案 + 非 .cn,选 .com。已备案域名指向境外服务器会被接入商核查判违规;.cn 对开 VPN 的人常走直连不通。
2. 系统初始化(安全基线)
SSH 上机,装齐并验证。命令写成 LF 换行的脚本再执行(Windows CRLF 会让远程路径带 \r 报假错):
ssh -i <SSH_KEY> root@<SERVER_IP>
fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
ufw default deny incoming && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable
mkdir -p /opt/ai-gateway/{apps,configs,logs,secrets,backups}
验证:docker --version、caddy version、ufw status(只 22/80/443)、swapon --show、systemctl is-active docker caddy fail2ban。
Docker + UFW 陷阱:Docker 发布端口会绕过 UFW。所以 Sub2API 一定只绑 127.0.0.1:8080,公网只留 Caddy 的 80/443。
3. 部署 Sub2API
不要跑陌生的远程一键脚本。下官方 compose + .env.example,手写 .env:
cd <APP_DIR>
chmod 600 .env
docker compose up -d
组成:sub2api + sub2api-postgres(postgres:18-alpine)+ sub2api-redis(redis:8-alpine),镜像 weishaw/sub2api:latest。
验证四连(全过再往下):
curl -s 127.0.0.1:8080/health
docker compose ps
curl -s http://127.0.0.1/health
curl -s http://<SERVER_IP>/ | grep -i sub2api
4. 域名 + 自动 HTTPS(Caddy)
- 加 DNS:域名服务商给
<DOMAIN> 加 A 记录 api → <SERVER_IP>(TTL 10 分钟)。
- 确认公网已能解析到真 IP(签证书依赖公网 DNS,别急着 reload):
ssh -i <SSH_KEY> root@<SERVER_IP> "getent hosts <API_HOST>"
- 写 Caddy 站点块(模板见 references/templates.md;
600s 超时是流式必须),caddy validate → systemctl reload caddy,Caddy 自动签 Let's Encrypt。
验证(--resolve 绕开本地 DNS 污染):
curl --resolve <API_HOST>:443:<SERVER_IP> https://<API_HOST>/
curl --resolve <API_HOST>:443:<SERVER_IP> https://<API_HOST>/v1/models
/v1/models 无 key 返回 401 是正确烟测结果,说明请求已进 Sub2API 授权层,不是故障。
5. 后台 / 数据库配置
DB 入口:docker compose exec -T postgres psql -U sub2api -d sub2api,配置都在 settings(key,value) 表。完整 SQL 见 templates.md。
绑域名后必须同时改 3 个 URL(否则邮件链接/API 文档/用户 base 仍旧址):api_base_url=https://<API_HOST>/v1、frontend_url 和 balance_low_notify_recharge_url=https://<API_HOST>。改完 docker compose restart sub2api(应用缓存公开配置,改库后必须重启应用容器,不动 PG/Redis)。
注册策略:先 registration_enabled=true 但 email_verify_enabled=false(SMTP 没通就强制验证码会把注册卡死)。SMTP 后台显示 smtp_password_configured=true 不等于能发信,test-smtp 通过前不开验证码/找回密码。
6. 账号治理
- 管理员:后台
POST /api/v1/admin/users 即使传 role=admin 也只建普通用户。必须数据库
UPDATE users SET role='admin' WHERE email='<ADMIN_EMAIL>'; 后重新登录验证 role=admin。
- 明文密码只存
/opt/ai-gateway/secrets/sub2api-admin.env(chmod 600),不进文档/git。
- 发给别人优先发普通用户账号或独立 API Key,绝不发管理员账号。
- 分发三阶段:① 人工发放 → ② 邀请码注册 → ③ 公开注册(支付/风控/验证码齐了才到)。
7. 上游渠道 + 分组(让 key 真能调模型)
注册开放 ≠ 能调模型。必须在后台配上游账号/渠道、分组、额度,key 才有东西可调。
- 后台加上游渠道(官方 API / MiniMax / DeepSeek / 智谱 / 供应商 key),按需建分组、设额度。
- 分组坑(客户端 403 根因):key 挂在
claude_code_only=true 之类分组只允许 /v1/messages;给 Codex/OpenAI-compatible 客户端用要放 default 分组,改完清 Redis apikey:auth:* 缓存再测。
- 对外 API Base 一定是
https://<API_HOST>/v1,不是根域名(填根域名命中前端 HTML 走错路由)。
8. 烟测(对外前必过)
无 key /v1/models 预期 401;有测试 key 列模型 200;再打一次短 prompt、一次长上下文、一次流式、一次故意失败看回退。烟测脚本骨架见 templates.md。
8b.(可选)把旧 CRS 服务器迁移成 Sub2API
可回滚流程:① 备份旧服务到 /root/crs-backups/<站>-to-sub2api-<时间>/;② 停旧服务(docker compose down 或 systemctl stop && disable,不删旧目录);③ 起 Sub2API;④ 反代改到 127.0.0.1:8080——用 Nginx 必须加 underscores_in_headers on;,否则 session_id 头被丢、粘性会话/缓存崩(Caddy 无此问题);⑤ 补 settings/管理员;⑥ 回滚=反代改回旧端口→停 sub2api→重启旧服务。
9. 运维 / 长期稳定
- SSH 别名(
~/.ssh/config)简化登录;云机频繁重连易被 fail2ban 断,合并成单次连接、间隔操作。
- 升级:
docker compose pull sub2api && up -d,只重建应用容器不动数据;升级前备份 compose + pg_dump。
- 1C2G "高峰重启"根因(不是容器重启):系统
dnf/apt makecache、云厂商 agent 自动更新抢内存触发 OOM killer。修法(命令见 templates.md):禁相关 timer、agent 脚本改不可执行、vm.swappiness=10、加到 3GB swap。
- 欠费会 suspend + power off 机器,付款后要手动开机;起来后 Caddy 先 502 等 8080 就绪属正常 → 配 billing 提醒。
反复救命的"验证心智"(整个流程通用)
/v1/models 无 key 返回 401 是对的,别当故障。
- 本地 DNS 会被代理污染(返回
198.18.x.x 等假 IP)→ 用服务器 getent hosts、公共 DoH、curl --resolve 交叉验证。
- 线上到底跑什么,必须
docker exec <容器> env / 实地核验,绝不从 git 仓库静态推断。
- 生产
docker-compose.yml 里的环境变量别提交进 git;若被跟踪,git rm --cached + .gitignore,新密钥永不进仓库。
- 每个动作做完用确定性命令逐项复核(
id/ls -l/stat/docker compose ps/curl),不信管道吞退出码的假 "OK"。
How to think(遇到判断题)
- 分层排查:一个"不可用"拆成 客户端 / 反代层(header、端口)/ Sub2API 应用 / 上游 provider(529、限额)/ 网络 五层逐层证伪,别一锅端。
- 先证伪"被墙/被攻击":单点异常先看服务器端日志和来源 IP 是否到达,再谈网络。
- 完成的定义是"能回滚、能核验、能撤销",不是"能打开一次"。公网 health ok 只证明入口可达,不证明生产可用。