NewAPI+Sub2API手把手部署搭建教程
免责说明本教程仅用于学习使用请勿用于商业用途、生产用途或其他不合规用途。由此导致的任何损失或者风险本人不承担任何责任。强烈建议优先阅读以下两个教程可以帮助理解本文教程过程newapi:https://zhuanlan.zhihu.com/p/2028786961842751352sub2api 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 ar /etc/apt/keyrings/docker.asc写 apt 源自动用清华镜像拉不通再换官方命令顺序如下CODENAME$(. /etc/os-release echo $VERSION_CODENAME)ARCH$(dpkg --print-architecture)REPOhttps://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntucurl -fsSI --connect-timeout 5 $REPO/dists/$CODENAME/Release /dev/null \ || REPOhttps://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接下来需要安装 Dockersudo 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验证dockerdocker --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 Mirrorslog-opts 限制单容器日志最多 30 MB10×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_USERnewapiMYSQL_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?charsetutf8mb4parseTimeTruelocLocal 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-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --innodb-buffer-pool-size128M - --max-connections200 - --performance-schemaOFF 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-size128M默认会自适应到 25% 内存 - MySQL --performance-schemaOFF省 ~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_USERrootROOT_PASSWORDWesky-$(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 uidLOGIN_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_RESPUID$(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 / 首页 / aboutH-H Content-Type:application/json -H Accept:application/json -H New-Api-User:$UIDBhttp://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 copy; 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/h1p 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:pWeskyApi 由 Wesky 团队运营与维护。/p}; echo每条返回 {message:,success:true} 即成功。验证curl -sS http://127.0.0.1:3000/api/status \ | python3 -c import sys,json;djson.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 --mode0755 /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.servicesudo tee /etc/systemd/system/cloudflared-newapi.service /dev/null EOF[Unit]Descriptioncloudflared quick tunnel for new-api (trycloudflare.com)Afternetwork-online.target docker.serviceWantsnetwork-online.target[Service]TypesimpleExecStart/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:3000 --no-autoupdate --metrics 127.0.0.1:20241Restarton-failureRestartSec5UserrootStandardOutputappend:/var/log/cloudflared-newapi.logStandardErrorappend:/var/log/cloudflared-newapi.logExecStartPre/bin/sh -c command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared[Install]WantedBymulti-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 URLfor 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-alpineSub2API 强依赖- 端口仅绑 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_EMAILadminweskyapi.localADMIN_PASSWORDWesky-$(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.ymlset -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_buffers64MB - -c - effective_cache_size192MB - -c - max_connections80 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: 128mbPG18 默认 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 tunnelsystemd 单元 cloudflared-sub2api.servicesudo tee /etc/systemd/system/cloudflared-sub2api.service /dev/null EOF[Unit]Descriptioncloudflared quick tunnel for Sub2API (trycloudflare.com)Afternetwork-online.target docker.serviceWantsnetwork-online.target[Service]TypesimpleExecStart/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:8080 --no-autoupdate --metrics 127.0.0.1:20242Restarton-failureRestartSec5UserrootStandardOutputappend:/var/log/cloudflared-sub2api.logStandardErrorappend:/var/log/cloudflared-sub2api.logExecStartPre/bin/sh -c command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared[Install]WantedBymulti-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拿 URLfor 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 --typeservice --staterunning | 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 | RedisDB0newapi, DB1sub2api || 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 URLsudo 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-tablesfalse 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 21升级到正式域名Named Tunneltrycloudflare.com 临时域名每次重启 cloudflared 都换不适合生产。要稳定走自有域名域名上 Cloudflare1. 在 [dash.cloudflare.com](https://dash.cloudflare.com) → Add a Site2. 把域名 NS 改到 Cloudflare 提供的两条 NS3. 等 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 unitsudo systemctl stop cloudflared-newapi.service cloudflared-sub2api.servicesudo tee /etc/systemd/system/cloudflared.service /dev/null EOF[Unit]Descriptioncloudflared (named tunnel)Afternetwork-online.target docker.serviceWantsnetwork-online.target[Service]TypesimpleExecStart/usr/local/bin/cloudflared tunnel run --token 粘贴你的TOKENRestarton-failureRestartSec5Userroot[Install]WantedBymulti-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同步 ServerAddressnew-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-size128M加 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 是固定值- [ ] 部署完后改 SSHPasswordAuthentication 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-apihttps://github.com/Calcium-Ion/new-api- Sub2APIhttps://github.com/Wei-Shaw/sub2api- Cloudflare Tunnelhttps://developers.cloudflare.com/cloudflare-one/connections/connect-networks/知乎图文简易教程链接newapi:https://zhuanlan.zhihu.com/p/2028786961842751352sub2api https://zhuanlan.zhihu.com/p/2028855400531764647