OmniRoute Fly.io 部署实战指南:flyctl 发布流程、Secrets 配置与 /data 持久化
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
OmniRoute 是面向多模型路由的 AI 网关(单端点聚合 352 家 Provider、1200+ 模型),其仓库内自带了可直接上云的Dockerfile与fly.toml。本文以仓库中已验证的omniroute应用配置为蓝本,完整讲解如何用本地flyctl把当前项目发布到 Fly.io:从工具安装、首次部署、Secrets 参数体系,到 fork 同步上游后的增量发布、发布后检查与故障排查,帮助你在半小时内跑通"一次部署、长期可升级"的生产环境。
1. 部署目标与整体方案
本文记录的部署方案基于仓库中已验证通过的配置,核心目标如下:
- 平台:Fly.io
- 部署方式:本地
flyctl直接发布,不依赖 CI 流水线 - 运行方式:复用仓库内现有的
Dockerfile与fly.toml,不额外编写基础设施代码 - 数据持久化:Fly Volume 挂载到容器内
/data,数据库与密钥文件均落盘 - 访问地址:
https://omniroute.fly.dev/
文档覆盖三类场景:首次把当前项目部署到 Fly.io、后续代码更新后的增量发布、以及新项目复用同一套流程部署。
2. 仓库内 fly.toml 关键配置解读
仓库根目录下的 fly.toml 是 Fly.io 部署的唯一配置源,其中已验证包含以下关键项(部署指南中的最小配置):
app = 'omniroute' primary_region = 'sin' [[mounts]] source = 'data' destination = '/data' [processes] app = 'node run-standalone.mjs' [http_service] internal_port = 20128 [env] TZ = "Asia/Shanghai" HOST = "0.0.0.0" HOSTNAME = "0.0.0.0" BIND = "0.0.0.0"逐项含义:
app = 'omniroute':决定实际发布到哪个 Fly 应用,也是所有flyctl命令-a参数的默认目标;destination = '/data':决定持久卷的挂载目录,本项目必须让DATA_DIR=/data,否则数据库和密钥会写入容器临时目录,重启即丢失;[processes] app = 'node run-standalone.mjs':定义主进程启动命令,对应仓库 scripts/dev/run-standalone.mjs 入口;[http_service] internal_port = 20128:容器内服务监听端口,与 Dockerfile 中的EXPOSE 20128、ENV PORT=20128保持一致。
实际仓库的fly.toml还包含若干生产强化项,可一并参考:
[[mounts]] source = 'data' destination = '/data' auto_extend_size_threshold = 80 auto_extend_size_increment = '1GB' auto_extend_size_limit = '10GB' [http_service] force_https = true auto_stop_machines = 'stop' auto_start_machines = true min_machines_running = 1 processes = ['app'] [[vm]] memory = '1gb' cpu_kind = 'shared' cpus = 1- 卷容量在使用率达到 80% 时自动扩容,每次增量 1GB、上限 10GB;
- HTTP 服务强制 HTTPS,机器空闲时可自动停止、有请求时自动启动,且至少保留 1 台运行;
- VM 规格为 1 vCPU / 1GB 内存,对应 Dockerfile 中默认的
OMNIROUTE_MEMORY_MB=1024堆上限。
3. 启动链路与持久化原理:为什么 DATA_DIR 必须为 /data
理解启动链路有助于排查部署问题。容器启动后按如下顺序执行:
- Dockerfile 的
ENTRYPOINT执行check-permissions.sh(校验挂载卷属主,防止非 root 用户无法写入/data); CMD执行node dev/run-standalone.mjs,即仓库 scripts/dev/run-standalone.mjs;- 该脚本首先调用
bootstrapEnv(),随后拉起server-ws.mjs(带 WebSocket 桥接包装的 Next.js standalone 服务)或回退到server.js。
bootstrapEnv()的实现位于 scripts/build/bootstrap-env.mjs,它是"密钥持久化"的关键:
- 若
JWT_SECRET、STORAGE_ENCRYPTION_KEY、API_KEY_SECRET缺失,会自动生成安全随机值; - 生成的密钥会写入
{DATA_DIR}/server.env,日志输出[bootstrap] Secrets persisted to: <路径>; - 环境变量优先级从低到高为:自动生成默认值 →
{DATA_DIR}/server.env(首次启动持久化)→ 偏好.env(DATA_DIR/.env→~/.omniroute/.env→./.env)→process.env(Docker-e/ Fly Secrets,最高优先级)。
数据库初始化后,src/lib/db/core.ts 会打印:
[DB] SQLite database ready: <sqliteFile> (DATA_DIR=<absolute>, SQLITE_FILE=<absolute>)因此在 Fly.io 上,DATA_DIR=/data是唯一的正确取值:它保证server.env(运行时密钥)与storage.sqlite(SQLite 数据库)都落在 Fly Volume 上,机器重建、滚动更新后数据不丢。若日志中出现/app/data/...(Dockerfile 默认值),说明环境变量被容器内默认值覆盖,需立即修正。
4. 必备工具:安装 Fly CLI 并登录
4.1 安装 Fly CLI
Windows PowerShell 下执行:
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"如果安装脚本在当前环境失败,也可以手动下载flyctl二进制并加入PATH。
4.2 登录 Fly 账号
flyctl auth login4.3 检查登录状态
flyctl auth whoami flyctl versionwhoami输出应显示你的 Fly 账号邮箱,version输出flyctl版本号。
5. 首次部署当前项目
5.1 获取代码并进入目录
git clone https://gitcode.com/GitHub_Trending/om/OmniRoute.git cd OmniRoute5.2 确认应用名
打开 fly.toml,重点确认这一行:
app = 'omniroute'如果你准备部署到自己的新应用,可改成全局唯一名称,例如:
app = 'omniroute-yourname'注意:Fly 控制台里查看的应用必须与fly.toml中的app一致;如果之前用过其他名字(例如oroute),不要与omniroute混淆。
5.3 创建应用
如果该应用尚不存在:
flyctl apps create omniroute若你已改名,把omniroute替换成你的应用名。
5.4 首次部署
flyctl deployflyctl会依据fly.toml构建镜像(走仓库 Dockerfile,多阶段构建,产物为 Next.js standalone 包)、创建或复用名为data的 Fly Volume 并挂载到/data,然后启动进程node run-standalone.mjs。
6. 必配参数与推荐参数体系
本项目在 Fly.io 上至少应配置以下参数,全部通过 Fly Secrets 注入(最终以环境变量形式进入process.env,优先级最高)。
6.1 已验证使用的参数
以下参数已在当前omniroute应用上实际部署验证:
API_KEY_SECRET— API Key 生成与校验DATA_DIR— 数据目录,必须为/dataJWT_SECRET— 登录态与 JWT 签名MACHINE_ID_SALT— 生成稳定机器标识NEXT_PUBLIC_BASE_URL— 调度器、前端回调等使用的公网地址OMNIROUTE_WS_BRIDGE_SECRET— 生产必需,WebSocket 桥接认证密钥STORAGE_ENCRYPTION_KEY— 加密存储敏感连接信息
6.2 参数职责与源码依据
| 变量名 | 是否推荐 | 说明 | 源码依据 |
|---|---|---|---|
API_KEY_SECRET | 必需 | API Key 生成与校验使用 | 缺失时由 bootstrap-env.mjs 自动生成并持久化 |
JWT_SECRET | 必需 | 登录态和 JWT 签名使用 | 同上,自动生成为 64 字节 hex |
STORAGE_ENCRYPTION_KEY | 强烈推荐 | 加密存储敏感连接信息(AES-256-GCM 字段加密) | 同上;若库中已有enc:v1:加密凭据而密钥丢失,启动时会拒绝自动生成并提示恢复 |
MACHINE_ID_SALT | 推荐 | 生成稳定机器标识 | src/shared/utils/machineId.ts 中getConsistentMachineId用salt || process.env.MACHINE_ID_SALT || "endpoint-proxy-salt"对机器原始 ID 做 SHA-256 后取前 16 字符 |
OMNIROUTE_WS_BRIDGE_SECRET | 生产必需 | WebSocket 桥接认证密钥 | src/app/api/internal/codex-responses-ws/route.ts 读取该变量并对收到的 secret 做 SHA-256 恒定时间比对,缺失会导致 WS 桥接握手失败 |
INITIAL_PASSWORD | 可选 | 首次部署时直接指定后台初始密码 | 未设置时 bootstrap-env.mjs 打印"默认密码 CHANGEME"警告 |
| OAuth/API 私密凭证 | 按需 | 各类外部平台鉴权配置 | 如ANTIGRAVITY_OAUTH_CLIENT_SECRET、QODER_OAUTH_CLIENT_SECRET等可选 OAuth 密钥 |
6.3 当前项目推荐值
| 变量名 | 推荐值 |
|---|---|
DATA_DIR | /data |
NEXT_PUBLIC_BASE_URL | https://omniroute.fly.dev |
DATA_DIR=/data非常关键,必须与 Fly Volume 挂载点一致,否则数据库与密钥落入容器临时目录;NEXT_PUBLIC_BASE_URL用于调度器回调、前端回调等场景,部署后若需要开启 OAuth 类 Provider(如 Antigravity、Gemini、Cursor),必须在 Provider 控制台把回调地址配置为<NEXT_PUBLIC_BASE_URL>/callback——所有 OAuth Provider 共用这一个回调路径,不存在按 Provider 区分的独立回调路由。
6.4 关于 INITIAL_PASSWORD
当前项目部署时没有设置INITIAL_PASSWORD。不设置的表现:
- 启动日志会提示默认密码是
CHANGEME; - 部署后应尽快在系统设置中修改登录密码。
如果希望无人值守初始化后台密码,可以补设:
flyctl secrets set INITIAL_PASSWORD=你的强密码 -a omniroute7. 一键设置参数
下面命令会生成安全随机值,并把当前项目需要的参数一次性写入 Fly Secrets(不包含INITIAL_PASSWORD,适用于应用名omniroute):
$apiKeySecret = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower() $jwtSecret = [Convert]::ToHexString((1..64 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower() $machineIdSalt = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower() $storageKey = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower() $wsBridgeSecret = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower() flyctl secrets set ` API_KEY_SECRET=$apiKeySecret ` JWT_SECRET=$jwtSecret ` MACHINE_ID_SALT=$machineIdSalt ` STORAGE_ENCRYPTION_KEY=$storageKey ` OMNIROUTE_WS_BRIDGE_SECRET=$wsBridgeSecret ` DATA_DIR=/data ` NEXT_PUBLIC_BASE_URL=https://omniroute.fly.dev ` -a omniroute说明:
- 各密钥的长度选择与 bootstrap-env.mjs 的自动生成规格一致(
API_KEY_SECRET、STORAGE_ENCRYPTION_KEY为 32 字节 hex,JWT_SECRET为 64 字节 hex),确保与既有约定兼容; OMNIROUTE_WS_BRIDGE_SECRET生产环境必须设置,缺失会破坏 WebSocket 桥接握手。
Linux / macOS 下也可以用openssl rand -hex 32生成:
flyctl secrets set OMNIROUTE_WS_BRIDGE_SECRET=$(openssl rand -hex 32) -a omniroute如果你还要加初始密码:
flyctl secrets set INITIAL_PASSWORD=你的强密码 -a omniroute8. 查看当前参数
flyctl secrets list -a omniroute如果 Fly 控制台的Secrets页面没有显示你期待的变量,先检查两点:
- 看的应用是不是
omniroute(而不是旧的oroute等其他应用); fly.toml里的app是否和控制台应用一致。
9. 后续更新发布
代码有更新后,发布步骤很简单:
git pull flyctl deploy如果只更新参数、不改代码:
flyctl secrets set KEY=value -a omnirouteFly 会自动对机器执行滚动更新。
9.1 跟踪原仓库更新并保留 fork 的 fly.toml
如果当前仓库是 fork,需要同步上游diegosouzapw/OmniRoute的更新,推荐按下面流程执行。
先确认远程:
git remote -v应至少包含:
origin指向你自己的 fork;upstream指向原仓库。
如果没有upstream,先添加:
git remote add upstream https://gitcode.com/GitHub_Trending/om/OmniRoute.git同步上游前,先抓取最新提交和标签:
git fetch upstream --tags查看当前版本和上游标签:
git describe --tags --always git show --no-patch --oneline v3.4.7如果你想合并上游最新main,并强制保留 fork 当前的fly.toml,按下面流程执行:
git merge upstream/main git checkout HEAD~1 -- fly.toml git add -- fly.toml git commit -m "chore(deploy): keep fork fly.toml" git push origin main说明:
git merge upstream/main用于同步原仓库最新代码;git checkout HEAD~1 -- fly.toml用于恢复合并前你 fork 自己的fly.toml;- 如果上游没有改
fly.toml,这一步不会带来额外差异; - 如果上游改了
fly.toml,这一步能确保 Fly 应用名、挂载卷、区域等 fork 自定义部署配置不被覆盖。
如果你明确只想对齐某个发布标签(例如v3.4.7),可以先确认标签是否已包含在upstream/main:
git merge-base --is-ancestor v3.4.7 upstream/main返回成功表示upstream/main已经包含该版本,直接合并upstream/main即可。实际操作时建议以当前仓库最新版本标签为准(如:latest或对应版本号),文中的v3.4.7仅作为历史示例保留。
9.2 同步上游后的标准发布顺序
同步原仓库完成后,推荐按下面顺序发布:
git fetch upstream --tagsgit merge upstream/main- 恢复 fork 的
fly.toml git push origin mainflyctl deployflyctl status -a omnirouteflyctl logs --no-tail -a omniroute
这就是当前项目升级时使用的实际流程。
10. 发布后检查
10.1 查看应用状态
flyctl status -a omniroute10.2 查看启动日志
flyctl logs --no-tail -a omniroute10.3 检查网站可访问
try { (Invoke-WebRequest -Uri "https://omniroute.fly.dev" -MaximumRedirection 5 -UseBasicParsing).StatusCode } catch { if ($_.Exception.Response) { $_.Exception.Response.StatusCode.value__ } else { throw } }返回200说明站点已正常响应。若使用自定义域名,把 URL 替换为对应域名。
11. 成功标志:日志中的两条关键记录
部署成功后,启动日志里应看到类似内容:
[bootstrap] Secrets persisted to: /data/server.env [DB] SQLite database ready: /data/storage.sqlite这两点很关键:
/data/server.env说明运行时密钥落到了持久卷(由 scripts/build/bootstrap-env.mjs 写入);/data/storage.sqlite说明数据库写入持久卷(由 src/lib/db/core.ts 打印并附上DATA_DIR、SQLITE_FILE绝对值便于核对卷拓扑)。
如果你看到的是/app/data/...,说明DATA_DIR没配对,需要立即修正——此时数据写入了容器可写层而非 Fly Volume,机器重建即丢失。
12. 常见问题排查
12.1 Secrets 页面是空的
通常有两种原因:
- 你还没执行
flyctl secrets set; - 你打开的是另一个应用,例如
oroute,不是omniroute。
12.2 flyctl deploy 报 app not found
先创建应用:
flyctl apps create omniroute12.3 fly.toml 解析失败
重点检查:
- 注释里是否有乱码字符;
- TOML 引号和缩进是否正确。
12.4 数据没有持久化
检查以下两点:
fly.toml中是否存在destination = '/data';DATA_DIR是否设置为/data。
二者缺一不可:卷挂载点与数据目录不一致时,日志会出现/app/data/...路径(见第 11 节)。
12.5 不设置 INITIAL_PASSWORD 是否能跑
可以运行,但会回退到默认密码CHANGEME(bootstrap-env.mjs 会打印相应警告)。生产环境建议尽快在系统设置中修改后台密码,或直接补设INITIAL_PASSWORD后重新部署。
13. 新项目复用建议
如果以后是新项目照着这份文档部署,最少改这几项:
- 修改 fly.toml 里的
app; - 修改
NEXT_PUBLIC_BASE_URL为你的公网域名; - 保持
DATA_DIR=/data,与卷挂载点严格一致; - 重新生成
API_KEY_SECRET、JWT_SECRET、MACHINE_ID_SALT、STORAGE_ENCRYPTION_KEY(生产环境再加OMNIROUTE_WS_BRIDGE_SECRET); - 首次部署后检查日志是否写入
/data。
不要直接复用旧项目的密钥:密钥泄漏会波及 API Key 体系、JWT 会话与加密存储的敏感连接信息。
14. 当前项目的最小发布清单
当前项目后续最常用的命令如下:
flyctl auth whoami flyctl status -a omniroute flyctl secrets list -a omniroute flyctl deploy flyctl logs --no-tail -a omniroute如果只是正常发版,核心就是:
flyctl deploy如果是新环境首次部署,核心步骤就是:
flyctl auth loginflyctl apps create omnirouteflyctl secrets set ... -a omnirouteflyctl deployflyctl logs --no-tail -a omniroute
延伸阅读
- 英文原版指南:docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md
- 简体中文版指南:docs/i18n/zh-CN/docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md
- 部署配置文件:fly.toml 与 Dockerfile
- 启动入口与密钥引导:scripts/dev/run-standalone.mjs、scripts/build/bootstrap-env.mjs
- 数据库初始化与状态日志:src/lib/db/core.ts
- 部署相关的其余运维文档可参考 docs/ops 目录(如 docs/ops/DOCKER_GUIDE.md、docs/ops/VM_DEPLOYMENT_GUIDE.md、docs/ops/REDIS_PRODUCTION_CONFIG.md)。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考