免责说明:本教程仅用于学习使用,请勿用于商业用途、生产用途或其他不合规用途。由此导致的任何损失或者风险,本人不承担任何责任。
强烈建议优先阅读以下两个教程,可以帮助理解本文教程过程:
newapi:
https://zhuanlan.zhihu.com/p/2028786961842751352
sub2api :
https://zhuanlan.zhihu.com/p/2028855400531764647
本人服务器环境: Ubuntu 24.04
使用下面命令,可以查看自己的服务器系统。建议云服务器使用硅谷节点,或者其他欧美地区的。采用靠谱厂家,但不要选择TX和ALI,也不要选择AWS等国际一线的。具体原由,可以自行查找A厂合规要求,以及O厂、G厂等等。
cat /etc/os-release | head -5安装 Docker + Docker Compose
先安装基础工具包
sudo apt-get update -ysudo apt-get install -y ca-certificates curl gnupg lsb-release vim加 Docker 官方 GPG(中国大陆建议清华源)
sudo install -m 0755 -d /etc/apt/keyrings优先官方,失败自动尝试清华
sudo curl -fsSL --connect-timeout 8 https://download.docker.com/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc \或者
sudo curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc文件授权
sudo chmod a+r /etc/apt/keyrings/docker.asc写 apt 源(自动用清华镜像,拉不通再换官方),命令顺序如下
CODENAME=$(. /etc/os-release && echo "$VERSION_CODENAME")ARCH=$(dpkg --print-architecture)REPO=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntucurl -fsSI --connect-timeout 5 "$REPO/dists/$CODENAME/Release" >/dev/null \ || REPO=https://download.docker.com/linux/ubuntuecho "deb [arch=$ARCH signed-by=/etc/apt/keyrings/docker.asc] $REPO $CODENAME stable" \ | sudo tee /etc/apt/sources.list.d/docker.listsudo apt-get update -y接下来需要安装 Docker
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-pluginsudo systemctl enable --now dockersudo usermod -aG docker $USER验证docker
docker --versiondocker compose version> ⚠️ `usermod -aG docker` 加组之后,**重新登录 SSH** 后才不用 sudo 跑 docker。本教程后面命令仍带 `sudo` 兼容。
配置镜像加速
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.1panel.live" ], "log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}}sudo systemctl restart dockerdocker info | grep -A3 'Registry Mirrors'`log-opts` 限制单容器日志最多 30 MB(10×3),避免长跑日志撑爆磁盘。
部署 new-api
创建目录
sudo mkdir -p /opt/new-api/{data,logs,mysql,redis}sudo chown -R $USER:$USER /opt/new-apicd /opt/new-api生成密码并保存
执行下面这段会一次性生成 4 个随机密码并写入 `.env.secret`,**只生成一次,丢了就要全部重置**:
cat > /opt/new-api/.env.secret <<EOFMYSQL_ROOT_PASSWORD=$(openssl rand -base64 24 | tr -d '=+/' | cut -c1-24)MYSQL_USER=newapiMYSQL_PASSWORD=$(openssl rand -base64 24 | tr -d '=+/' | cut -c1-24)SESSION_SECRET=$(openssl rand -hex 32)chmod 600 /opt/new-api/.env.secretcat /opt/new-api/.env.secret # 复制到自己的密码管理器把上面输出的 4 行抄到密码管理器(KeePass、1Password 等)。
写 docker-compose.yml
# 加载刚生成的密码到 shell 变量
set -a; source /opt/new-api/.env.secret; set +acat > /opt/new-api/docker-compose.yml <<EOFservices: new-api: image: calciumion/new-api:latest container_name: new-api restart: unless-stopped depends_on: mysql: condition: service_healthy redis: condition: service_started ports: - "3000:3000" environment: TZ: Asia/Shanghai SQL_DSN: "newapi:${MYSQL_PASSWORD}@tcp(mysql:3306)/new-api?charset=utf8mb4&parseTime=True&loc=Local" REDIS_CONN_STRING: "redis://redis:6379/0" SESSION_SECRET: "${SESSION_SECRET}" CRYPTO_SECRET: "${SESSION_SECRET}" SYSTEM_NAME: "WeskyApi" GIN_MODE: release ERROR_LOG_ENABLED: "true" volumes: - ./data:/data - ./logs:/app/logs healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/status >/dev/null || exit 1"] interval: 30s timeout: 5s retries: 10 mysql: image: mysql:8.4 container_name: new-api-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}" MYSQL_DATABASE: "new-api" MYSQL_USER: "newapi" MYSQL_PASSWORD: "${MYSQL_PASSWORD}" TZ: Asia/Shanghai command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --innodb-buffer-pool-size=128M - --max-connections=200 - --performance-schema=OFF volumes: - ./mysql:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 20 redis: image: redis:7-alpine container_name: new-api-redis restart: unless-stopped command: ["redis-server", "--maxmemory", "128mb", "--maxmemory-policy", "allkeys-lru", "--save", "900", "1"] volumes: - ./redis:/dataEOF> 关键调优(小内存机器必须):
> - MySQL `--innodb-buffer-pool-size=128M`:默认会自适应到 25% 内存> - MySQL `--performance-schema=OFF`:省 ~50 MB> - Redis `--maxmemory 128mb` + LRU:防止无限增长拉镜像并启动
cd /opt/new-apisudo docker compose pull # 首次约 200 MB,国内 1-3 分钟sudo docker compose up -dsudo docker compose ps等服务就绪
for i in $(seq 1 36); do if curl -fsS http://127.0.0.1:3000/api/status >/dev/null; then echo "ok"; break fi echo "等待中 $i/36"; sleep 5done成功的话最后显示 `ok`,否则看日志:
sudo docker logs --tail 100 new-apisudo docker logs --tail 100 new-api-mysql完成首次初始化(创建 root 管理员)
**当前版本 new-api 不再预置默认账户**,必须主动调 `/api/setup` 接口。先生成 root 密码:
ROOT_USER=rootROOT_PASSWORD="Wesky-$(openssl rand -base64 16 | tr -d '=+/' | cut -c1-16)"echo "ROOT_USERNAME=$ROOT_USER" | sudo tee -a /opt/new-api/.env.secretecho "ROOT_PASSWORD=$ROOT_PASSWORD" | sudo tee -a /opt/new-api/.env.secretecho ">>> 务必记下: $ROOT_USER / $ROOT_PASSWORD"调用 setup 接口:
curl -sS -X POST http://127.0.0.1:3000/api/setup \ -H 'Content-Type: application/json' \ -d "$(cat <<EOF{"Username":"$ROOT_USER","Password":"$ROOT_PASSWORD","ConfirmPassword":"$ROOT_PASSWORD","SelfUseModeEnabled":false,"DemoSiteEnabled":false}EOF)"echo期望返回 `{"message":"系统初始化成功","success":true}`。
登录拿 cookie + uid
LOGIN_RESP=$(curl -sS -c /tmp/newapi_cookie.txt -X POST http://127.0.0.1:3000/api/user/login \ -H 'Content-Type: application/json' \ -d "{\"username\":\"$ROOT_USER\",\"password\":\"$ROOT_PASSWORD\"}")echo "$LOGIN_RESP"UID=$(echo "$LOGIN_RESP" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["id"])')echo "UID=$UID"> ⚠️ **关键坑**:调任何管理员接口都要带 `New-Api-User: $UID` 这个 HTTP 头,仅 cookie 不够。否则会得到 `Unauthorized, New-Api-User header not provided`。
写入品牌信息(system_name / footer / 首页 / about)
H="-H Content-Type:application/json -H Accept:application/json -H New-Api-User:$UID"B=http://127.0.0.1:3000例如把newapi标题,改为WeskyApi
# system_namecurl -sS -b /tmp/newapi_cookie.txt -X PUT $B/api/option/ $H \ -d '{"key":"SystemName","value":"WeskyApi"}'; echo# Logo(留空 → 前端渲染 SystemName 文字)curl -sS -b /tmp/newapi_cookie.txt -X PUT $B/api/option/ $H \ -d '{"key":"Logo","value":""}'; echo# 底栏curl -sS -b /tmp/newapi_cookie.txt -X PUT $B/api/option/ $H \ -d '{"key":"Footer","value":"<p style=\"text-align:center\">WeskyApi © 2026</p>"}'; echo# 首页内容curl -sS -b /tmp/newapi_cookie.txt -X PUT $B/api/option/ $H \ -d '{"key":"HomePageContent","value":"<div style=\"text-align:center;padding:48px 20px\"><h1 style=\"font-size:42px;letter-spacing:4px;color:#00e6ff\">WeskyApi</h1><p style=\"color:#9ab;margin-top:12px\">统一大模型 API 中转服务 · 稳定 · 高速</p></div>"}'; echo# 关于curl -sS -b /tmp/newapi_cookie.txt -X PUT $B/api/option/ $H \ -d '{"key":"About","value":"<p>WeskyApi 由 Wesky 团队运营与维护。</p>"}'; echo每条返回 `{"message":"","success":true}` 即成功。
验证
curl -sS http://127.0.0.1:3000/api/status \ | python3 -c 'import sys,json;d=json.load(sys.stdin)["data"];print("system_name=",d["system_name"]);print("footer_html=",d["footer_html"][:80])'期望 `system_name= WeskyApi`。
安装 cloudflared 并暴露 new-api
装 cloudflared (Cloudflare 官方 apt 源)
sudo mkdir -p --mode=0755 /usr/share/keyringscurl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \ | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/nullecho 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' \ | sudo tee /etc/apt/sources.list.d/cloudflared.listsudo apt-get update -ysudo apt-get install -y cloudflaredcloudflared --version # 期望 2026.x写 systemd 单元 `cloudflared-newapi.service`
sudo tee /etc/systemd/system/cloudflared-newapi.service >/dev/null <<'EOF'[Unit]Description=cloudflared quick tunnel for new-api (trycloudflare.com)After=network-online.target docker.serviceWants=network-online.target[Service]Type=simpleExecStart=/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:3000 --no-autoupdate --metrics 127.0.0.1:20241Restart=on-failureRestartSec=5User=rootStandardOutput=append:/var/log/cloudflared-newapi.logStandardError=append:/var/log/cloudflared-newapi.logExecStartPre=/bin/sh -c 'command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared'[Install]WantedBy=multi-user.targetEOFsudo : > /var/log/cloudflared-newapi.logsudo chmod 640 /var/log/cloudflared-newapi.logsudo systemctl daemon-reloadsudo systemctl enable --now cloudflared-newapi.servicesudo systemctl status cloudflared-newapi.service --no-pager | head -10> `--metrics 127.0.0.1:20241`:第一个 tunnel 占 20241,第二个用 20242,**两个不能撞**。
拿到 trycloudflare URL
for i in $(seq 1 30); do URL=$(sudo grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /var/log/cloudflared-newapi.log | head -1) [ -n "$URL" ] && { echo "URL: $URL"; break; } sleep 3doneecho "$URL" | sudo tee /opt/new-api/tunnel_url.txt```映射的公网域名地址会被写入到指定的txt文件内,例如
把 URL 写回 new-api(让 Token 详情页生成完整链接)
curl -sS -b /tmp/newapi_cookie.txt -X PUT http://127.0.0.1:3000/api/option/ \ -H 'Content-Type: application/json' -H "New-Api-User: $UID" \ -d "{\"key\":\"ServerAddress\",\"value\":\"$URL\"}"echo从你的电脑(不是服务器)验证
curl -sS https://你的tunnel域名.trycloudflare.com/api/status | head -c 200> ⚠️ 服务器自己 curl trycloudflare.com 可能解析失败(DNS 屏蔽),不影响外部访问。
到这里 NewApi即可整体上线了。浏览器打开 URL 用 root(或者你自己定义的其他用户名) + 生成的密码登录即可。
部署 Sub2API
关键设计
- 复用 new-api 的 Redis(不再起新的 Redis 容器)- 单独起 PostgreSQL 18-alpine(Sub2API 强依赖)- 端口仅绑 `127.0.0.1:8080`,强制走 cloudflared- Postgres 不暴露端口创建目录
sudo mkdir -p /opt/sub2api/{data,postgres}sudo chown -R $USER:$USER /opt/sub2apicd /opt/sub2api生成机密
cat > /opt/sub2api/.env.secret <<EOFPOSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '=+/' | cut -c1-24)JWT_SECRET=$(openssl rand -hex 32)TOTP_ENCRYPTION_KEY=$(openssl rand -hex 32)ADMIN_EMAIL=admin@weskyapi.localADMIN_PASSWORD=Wesky-$(openssl rand -base64 16 | tr -d '=+/' | cut -c1-16)EOFchmod 600 /opt/sub2api/.env.secretcat /opt/sub2api/.env.secret # 抄到密码管理器> - `JWT_SECRET` **必须固定**:变了所有用户被踢下线
> - `TOTP_ENCRYPTION_KEY` **必须固定**:变了所有 2FA 失效
> - `ADMIN_EMAIL` 是登录用户名
写 docker-compose.yml
set -a; source /opt/sub2api/.env.secret; set +acat > /opt/sub2api/docker-compose.yml <<EOFservices: sub2api: image: weishaw/sub2api:latest container_name: sub2api restart: unless-stopped ulimits: nofile: { soft: 65535, hard: 65535 } ports: - "127.0.0.1:8080:8080" depends_on: postgres: condition: service_healthy volumes: - ./data:/app/data environment: AUTO_SETUP: "true" SERVER_HOST: "0.0.0.0" SERVER_PORT: "8080" SERVER_MODE: "release" RUN_MODE: "standard" DATABASE_HOST: postgres DATABASE_PORT: "5432" DATABASE_USER: sub2api DATABASE_PASSWORD: "${POSTGRES_PASSWORD}" DATABASE_DBNAME: sub2api DATABASE_SSLMODE: disable DATABASE_MAX_OPEN_CONNS: "30" DATABASE_MAX_IDLE_CONNS: "5" REDIS_HOST: new-api-redis REDIS_PORT: "6379" REDIS_DB: "1" REDIS_POOL_SIZE: "128" REDIS_MIN_IDLE_CONNS: "4" JWT_SECRET: "${JWT_SECRET}" JWT_EXPIRE_HOUR: "168" TOTP_ENCRYPTION_KEY: "${TOTP_ENCRYPTION_KEY}" ADMIN_EMAIL: "${ADMIN_EMAIL}" ADMIN_PASSWORD: "${ADMIN_PASSWORD}" TZ: Asia/Shanghai SECURITY_URL_ALLOWLIST_ENABLED: "false" SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP: "true" SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS: "true" networks: - sub2api-network - newapi-shared healthcheck: test: ["CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 5 start_period: 60s postgres: image: postgres:18-alpine container_name: sub2api-postgres restart: unless-stopped shm_size: 128mb environment: PGDATA: /var/lib/postgresql/data POSTGRES_USER: sub2api POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" POSTGRES_DB: sub2api TZ: Asia/Shanghai command: - postgres - -c - shared_buffers=64MB - -c - effective_cache_size=192MB - -c - max_connections=80 volumes: - ./postgres:/var/lib/postgresql/data networks: - sub2api-network healthcheck: test: ["CMD-SHELL", "pg_isready -U sub2api -d sub2api"] interval: 10s timeout: 5s retries: 10 start_period: 30snetworks: sub2api-network: driver: bridge newapi-shared: external: true name: new-api_defaultEOF> 三个不能省的细节:
> 1. `PGDATA: /var/lib/postgresql/data` 必须显式设置。`postgres:18-alpine` 默认 `PGDATA=/var/lib/postgresql/18/docker`,不显式设的话挂载到 `./postgres` 的卷里 **不会落数据**,重启就 initdb,账号订阅全丢。
> 2. `shm_size: 128mb`:PG18 默认 64MB 太小,复杂查询会报 shared memory 不足。
> 3. `networks.newapi-shared.external: true name: new-api_default`:把 sub2api 接入 new-api 的网络,才能用容器名 `new-api-redis` 解析到那个 redis 容器。
启动
cd /opt/sub2apisudo docker compose pull # 首次约 250 MBsudo docker compose up -dsudo docker compose ps期望两个容器都 `Up (healthy)`:
sub2api weishaw/sub2api:latest Up (healthy) 127.0.0.1:8080->8080/tcpsub2api-postgres postgres:18-alpine Up (healthy) 5432/tcp注意 `sub2api` 的 PORTS 列必须是 `127.0.0.1:8080`,不是 `0.0.0.0`。
等就绪 + 验证
for i in $(seq 1 36); do if curl -fsS http://127.0.0.1:8080/health >/dev/null; then echo ok; break; fi echo "等待中 $i"; sleep 5donecurl -sS http://127.0.0.1:8080/health # {"status":"ok"}启动失败时看日志:
sudo docker logs --tail 100 sub2apisudo docker logs --tail 100 sub2api-postgres暴露 Sub2API(第二个 cloudflared tunnel)
systemd 单元 `cloudflared-sub2api.service`
sudo tee /etc/systemd/system/cloudflared-sub2api.service >/dev/null <<'EOF'[Unit]Description=cloudflared quick tunnel for Sub2API (trycloudflare.com)After=network-online.target docker.serviceWants=network-online.target[Service]Type=simpleExecStart=/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:8080 --no-autoupdate --metrics 127.0.0.1:20242Restart=on-failureRestartSec=5User=rootStandardOutput=append:/var/log/cloudflared-sub2api.logStandardError=append:/var/log/cloudflared-sub2api.logExecStartPre=/bin/sh -c 'command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared'[Install]WantedBy=multi-user.targetEOFsudo : > /var/log/cloudflared-sub2api.logsudo chmod 640 /var/log/cloudflared-sub2api.logsudo systemctl daemon-reloadsudo systemctl enable --now cloudflared-sub2api.service与第一个单元的 3 处差异:
- `--url` 指向 `8080`
- `--metrics` 用 `20242`
- 日志路径 `cloudflared-sub2api.log`
拿 URL
for i in $(seq 1 30); do URL2=$(sudo grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /var/log/cloudflared-sub2api.log | head -1) [ -n "$URL2" ] && { echo "Sub2API URL: $URL2"; break; } sleep 3doneecho "$URL2" | sudo tee /opt/sub2api/tunnel_url.txt验证(从其他机器电脑访问)
curl -sS https://你的sub2api.trycloudflare.com/health # 期望: {"status":"ok"}浏览器打开此 URL,用 `.env.secret` 里的 `ADMIN_EMAIL` / `ADMIN_PASSWORD` 登录。
端口与服务清单(部署完成后查看)
# 容器sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'# systemd 服务systemctl list-units --type=service --state=running | grep -E '(docker|cloudflared)'# 内存占用free -h# 端口sudo ss -tlnp | grep -E ':(3000|8080|20241|20242) '正常情况:
| 服务 | 监听 | 用途 ||---|---|---|| `new-api` | `0.0.0.0:3000` | new-api Web/API || `sub2api` | `127.0.0.1:8080` | Sub2API Web/API || `cloudflared-newapi` | `127.0.0.1:20241` | 第一个 tunnel 指标端口 || `cloudflared-sub2api` | `127.0.0.1:20242` | 第二个 tunnel 指标端口 || `new-api-mysql` | 仅容器内 3306 | MySQL,不暴露 || `new-api-redis` | 仅容器内 6379 | Redis(DB0=newapi, DB1=sub2api) || `sub2api-postgres` | 仅容器内 5432 | PostgreSQL,不暴露 |日常运维
看日志
sudo docker logs -f --tail 100 new-apisudo docker logs -f --tail 100 sub2apisudo journalctl -u cloudflared-newapi.service -n 100 --no-pagersudo journalctl -u cloudflared-sub2api.service -n 100 --no-pagersudo tail -f /var/log/cloudflared-newapi.log /var/log/cloudflared-sub2api.log重启服务
# 应用容器sudo docker compose -f /opt/new-api/docker-compose.yml restart new-apisudo docker compose -f /opt/sub2api/docker-compose.yml restart sub2api# Tunnel(重启会换 trycloudflare URL!)sudo systemctl restart cloudflared-newapi.servicesudo systemctl restart cloudflared-sub2api.service# 拿新 URLcat /opt/new-api/tunnel_url.txtcat /opt/sub2api/tunnel_url.txt# 或直接从日志重抓sudo grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /var/log/cloudflared-newapi.log | tail -1sudo grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /var/log/cloudflared-sub2api.log | tail -1升级镜像
cd /opt/new-api && sudo docker compose pull && sudo docker compose up -dcd /opt/sub2api && sudo docker compose pull && sudo docker compose up -dsudo docker image prune -f备份
sudo mkdir -p /opt/backup# new-api MySQLsudo docker exec new-api-mysql sh -c \ 'MYSQL_PWD=$(grep ^MYSQL_PASSWORD /opt/new-api/.env.secret | cut -d= -f2) \ mysqldump -unewapi --single-transaction --quick --lock-tables=false new-api' \ | sudo tee /opt/backup/new-api-$(date +%F).sql >/dev/null# Sub2API PostgreSQLsudo docker exec sub2api-postgres pg_dump -U sub2api sub2api \ | gzip | sudo tee /opt/backup/sub2api-$(date +%F).sql.gz >/dev/null# 数据目录sudo tar czf /opt/backup/new-api-data-$(date +%F).tgz /opt/new-api/{data,logs}sudo tar czf /opt/backup/sub2api-data-$(date +%F).tgz /opt/sub2api/datals -lh /opt/backup/加 cron 每天凌晨备份:
sudo crontab -e# 加一行:0 3 * * * /usr/bin/bash -lc '/opt/scripts/backup.sh' >>/var/log/backup.log 2>&1升级到正式域名(Named Tunnel)
`trycloudflare.com` 临时域名每次重启 cloudflared 都换,不适合生产。要稳定走自有域名:
域名上 Cloudflare
1. 在 [dash.cloudflare.com](https://dash.cloudflare.com) → Add a Site
2. 把域名 NS 改到 Cloudflare 提供的两条 NS
3. 等 Zone 状态变 Active(一般 5-30 分钟)
在 Zero Trust 创建 Tunnel
[one.dash.cloudflare.com](https://one.dash.cloudflare.com) → Networks → Tunnels →
Create a tunnel → Cloudflared → 起个名(如 `vps-1`)→ 选 Linux 看到一段安装命令,里面有 `--token eyJhI...`,把 token 复制下来。
在Public Hostname标签页加两条记录:
| Subdomain | Domain | Type | URL ||---|---|---|---|| `api` | yourdomain.com | HTTP | `localhost:3000` || `pincc` | yourdomain.com | HTTP | `localhost:8080` |替换 systemd unit
sudo systemctl stop cloudflared-newapi.service cloudflared-sub2api.servicesudo tee /etc/systemd/system/cloudflared.service >/dev/null <<EOF[Unit]Description=cloudflared (named tunnel)After=network-online.target docker.serviceWants=network-online.target[Service]Type=simpleExecStart=/usr/local/bin/cloudflared tunnel run --token <粘贴你的TOKEN>Restart=on-failureRestartSec=5User=root[Install]WantedBy=multi-user.targetEOFsudo systemctl disable cloudflared-newapi.service cloudflared-sub2api.servicesudo systemctl daemon-reloadsudo systemctl enable --now cloudflared.servicesudo systemctl status cloudflared.service --no-pager | head -10同步 ServerAddress
new-api 上:
重新登录拿 cookie + UID,然后:
curl -sS -b /tmp/newapi_cookie.txt -X PUT http://127.0.0.1:3000/api/option/ \ -H 'Content-Type: application/json' -H "New-Api-User: $UID" \ -d '{"key":"ServerAddress","value":"https://api.yourdomain.com"}'故障排查速查
| 症状 | 可能原因 | 处置 ||---|---|---|| `docker compose pull` 卡死 | 镜像源不可达 | 第 2.5 节加加速器后 `sudo systemctl restart docker` || MySQL OOM 自动重启 | buffer-pool 没限制 | 确认 compose 里有 `--innodb-buffer-pool-size=128M`,加 swap || `Unauthorized, New-Api-User header not provided` | 调管理员接口忘带 header | 所有 `/api/option /api/user/...` 必须带 `-H "New-Api-User: $UID"` || 第二次跑 setup 报"用户名/密码不正确" | DB 已有 root,密码不对 | 用 `.env.secret` 里的 ROOT_PASSWORD,不要重新生成 || Sub2API 启动报 `POSTGRES_PASSWORD is required` | env 没带过去 | 检查 `set -a; source .env.secret; set +a` 后再 envsubst || Sub2API 重启数据全丢 | `PGDATA` 没显式设 | 第 5.4 节关键细节 1 || Sub2API 连不上 redis | external 网络名错 | `docker network ls` 看 new-api 的网络名是不是 `new-api_default` || Tunnel URL 突然不通 | cloudflared 重启换了 URL | `cat /opt/*/tunnel_url.txt` 拿新地址,或看日志 || 服务器自己 curl trycloudflare 失败 | 国内 DNS 屏蔽 | 不影响外部,从本机/手机访问验证 || 两个 tunnel 同时只起来一个 | metrics 端口撞了 | 第二个改成 20242 |安全清单
- [ ] `/opt/new-api/.env.secret` 与 `/opt/sub2api/.env.secret` 都是 `chmod 600`
- [ ] MySQL / Postgres / Redis 端口都没暴露宿主机
- [ ] 想更严的话把 new-api 端口也改成 `127.0.0.1:3000`
- [ ] 所有密码 ≥24 字节随机生成
- [ ] `JWT_SECRET` / `TOTP_ENCRYPTION_KEY` 是固定值
- [ ] 部署完后改 SSH:`PasswordAuthentication no`,只留密钥
- [ ] 启用 ufw 或腾讯云安全组:只开 22/80/443,封掉 3000/8080
- [ ] 备份脚本接 cron
- [ ] 留一条 `df -h` / `free -h` 的监控告警
文件位置速查
| 路径 | 用途 ||---|---|| `/opt/new-api/docker-compose.yml` | new-api 栈定义 || `/opt/new-api/.env.secret` | new-api MySQL/root 密码 || `/opt/new-api/data/` | new-api 应用数据 || `/opt/new-api/mysql/` | MySQL 数据 || `/opt/new-api/redis/` | Redis 持久化 || `/opt/new-api/tunnel_url.txt` | 当前 trycloudflare URL || `/opt/sub2api/docker-compose.yml` | Sub2API 栈定义 || `/opt/sub2api/.env.secret` | Sub2API JWT/Postgres/管理员密码 || `/opt/sub2api/data/` | Sub2API 应用数据 || `/opt/sub2api/postgres/` | PostgreSQL 数据 || `/opt/sub2api/tunnel_url.txt` | 当前 trycloudflare URL || `/etc/systemd/system/cloudflared-newapi.service` | new-api tunnel || `/etc/systemd/system/cloudflared-sub2api.service` | Sub2API tunnel || `/var/log/cloudflared-newapi.log` | new-api tunnel 日志 || `/var/log/cloudflared-sub2api.log` | Sub2API tunnel 日志 || `/etc/docker/daemon.json` | Docker 镜像加速 |参考链接
- new-api:https://github.com/Calcium-Ion/new-api- Sub2API:https://github.com/Wei-Shaw/sub2api- Cloudflare Tunnel:https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/知乎图文简易教程链接:
newapi:
https://zhuanlan.zhihu.com/p/2028786961842751352
sub2api :
https://zhuanlan.zhihu.com/p/2028855400531764647