搭一套自己的 AI 网关:New API + CLIProxyAPI 部署记录
这几天把自己的服务器重新整理了一遍,顺手搭了一套统一的 AI 调用入口。起因并不复杂:平时会用到 DeepSeek、Gemini、Codex、Claude Code、Kimi、Grok 等不同来源的模型和账号能力,如果每个客户端都单独配置 Base URL、Key 和模型名,时间一久就会很乱。
后来我把思路收敛成一句话:公网只留下一个稳定入口,真正敏感的账号态服务全部放在内网。这样 Cherry Studio、Cursor、手机脚本都只需要连 New API;至于后面到底走官方 API Key,还是走 CLIProxyAPI 转出来的账号能力,都交给服务器处理。
文中的域名、IP、密钥和订阅地址都做了替换。实际部署时请换成自己的信息,也请不要把自己的 API Key、管理密钥、OAuth 邮箱、代理订阅和宝塔面板地址放进公开网络中,这一点非常重要。
整体结构
最终结构分成五块:New API 做公网统一入口;CLIProxyAPI 把 CLI/OAuth 类账号能力转成 OpenAI-compatible API;Mihomo 给服务器侧服务提供代理出口;宝塔和 Nginx 负责域名、HTTPS、反向代理;SSH 隧道只负责打开内网管理端。
域名和反向代理
我给这套网关单独准备了一个二级域名,例如 ai.example.com。DNS 里只需要加一条 A 记录,把它指向服务器公网 IP。这里容易混淆的一点是:DNS 只负责域名到 IP,不负责端口。真正把请求转到哪个服务,是 Nginx 反向代理的工作。
New API 容器只绑定到服务器本机端口,公网不直接开放 3001。外部访问全部走宝塔站点的 HTTPS,这样证书、访问日志和反代规则都能在宝塔里统一看到。
部署 New API
New API 适合作为公网主入口。它负责令牌、渠道、模型映射、日志和额度这些“网关应该管的事”。我这里没有直接用服务器已有的 MySQL,而是让 New API 自己带 PostgreSQL 和 Redis。这样和原本的站点数据库互不影响,后续迁移也更清楚。
services:
new-api:
image: calciumion/new-api:latest
container_name: new-api
restart: unless-stopped
ports:
- "127.0.0.1:3001:3000"
environment:
SQL_DSN: postgres://newapi:${POSTGRES_PASSWORD}@new-api-postgres:5432/newapi
REDIS_CONN_STRING: redis://new-api-redis:6379
SESSION_SECRET: ${SESSION_SECRET}
depends_on:
- new-api-postgres
- new-api-redis
new-api-postgres:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_DB: newapi
POSTGRES_USER: newapi
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
new-api-redis:
image: redis:7-alpine
restart: unless-stopped
启动后打开 http://127.0.0.1:3001/setup 完成初始化。先接 DeepSeek、OpenRouter、百炼这类官方 API Key 渠道,确认 New API 自身能正常工作,再继续往后接 CLIProxyAPI。
部署 Mihomo
服务器侧代理我选 Mihomo。原因很朴素:订阅兼容性好,容器部署简单,也有现成的 Web 面板。它不需要暴露公网,主要给 CLIProxyAPI 访问部分上游服务时使用。
services:
mihomo:
image: metacubex/mihomo:latest
container_name: mihomo
restart: unless-stopped
volumes:
- ./config:/root/.config/mihomo
ports:
- "127.0.0.1:9090:9090"
- "127.0.0.1:7890:7890"
订阅地址和控制器密钥不要公开到外网中。实际维护时可以放到服务器本地配置文件,再通过脚本刷新订阅。Web 面板同样只通过 SSH 隧道打开。
部署 CLIProxyAPI
CLIProxyAPI 这层只做一件事:把 Codex、Claude Code、Gemini CLI、Kimi、Grok 等账号能力包装成兼容 API。它手里会保存认证文件和账号态,所以这层我只放在内网。
services:
cli-proxy-api:
image: ghcr.io/router-for-me/cli-proxy-api:v7.1.29
container_name: cli-proxy-api
restart: unless-stopped
ports:
- "127.0.0.1:8317:8317"
environment:
API_KEY: ${CPA_API_KEY}
MANAGEMENT_KEY: ${CPA_MANAGEMENT_KEY}
HTTP_PROXY: http://mihomo:7890
HTTPS_PROXY: http://mihomo:7890
volumes:
- ./auths:/app/auths
- ./data:/app/data
版本号建议固定到自己验证过的一版。升级前先看 release note,再拉新镜像;这种握着账号态的服务,不适合无脑追 latest。
用 SSH 隧道打开管理端
管理端口我统一放进 SSH 配置。这样平时只要执行 ssh -N aliyun,本地就能打开对应面板;关闭 SSH 后,这些入口也自然关掉。
Host aliyun
HostName your.server.ip
User ecs-user
LocalForward 3000 127.0.0.1:3000
LocalForward 8317 127.0.0.1:8317
LocalForward 9090 127.0.0.1:9090
把 CLIProxyAPI 接回 New API
这里有一个小细节:New API 在容器里运行时,容器里的 127.0.0.1 指的是 New API 自己,不是宿主机,也不是 CLIProxyAPI。更稳的做法是让两个容器加入同一个 Docker 网络,然后用服务名访问。
docker network connect cli-proxy-api_default new-api
配置完成后,不要只看模型列表。最可靠的验证方式,是从 New API 的公网地址发一次真实请求,让模型只回复 OK。
curl https://ai.example.com/v1/chat/completions -H "Authorization: Bearer sk-你的-newapi-token" -H "Content-Type: application/json" -d '{
"model": "your-model-name",
"messages": [
{"role": "user", "content": "只回复 OK"}
]
}'
客户端怎么用
对客户端来说,最后只需要三样东西:New API 的 Base URL、New API 里生成的令牌、以及在 New API 里暴露出来的模型名。
我现在更喜欢这种方式:客户端只拿 New API 令牌,不直接接触上游账号,也不接触 CLIProxyAPI 的管理密钥。哪个设备能用哪些模型、哪个渠道优先、是否限额,都放在 New API 里管。
几个值得记下来的点
第一,DNS 只管域名到 IP,端口转发交给 Nginx。很多 404 或跳到主站的问题,其实不是解析错了,而是宝塔站点或反向代理规则没匹配上。
第二,官方 API Key 和账号态资源最好分开管理。DeepSeek、OpenRouter 这类 Key 类服务直接进 New API;Codex、Gemini CLI、Claude Code 这类账号态资源先放 CLIProxyAPI,再由 New API 调用。
第三,容器网络要想清楚。跨容器访问时,优先用 Docker 网络里的服务名,不要把宿主机、本机回环地址和容器内部回环地址混在一起。
最后的状态
折腾完以后,使用体验其实变简单了:所有客户端只认一个入口,服务端再慢慢扩渠道、调模型、做限额。后续如果要接更多账号、换代理出口,改的也都是后端,不用再到每台设备上逐个重配;多电脑、多终端的情况下会方便很多。
这篇先记录到这里,大家可以参考。
参考
本文部分内容使用AI生成,请注意甄别。


