OmniRoute Fly.io 部署实战指南:首次发布、密钥配置与数据持久化运维
【免费下载链接】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 仓库中已实际验证通过的 Fly.io 部署指南(及其 孟加拉语译本),完整讲解如何将 OmniRoute 这个统一 AI 网关部署到 Fly.io:从flyctl环境准备、首次发布,到 Secrets 密钥体系、OAuth 回调配置、fork 同步发布与持久化验证。读完本文,你将掌握一套可直接复用的 Fly.io 云部署与滚动升级方案,并理解每个关键配置背后的源码实现原理。
1. 部署目标
- 平台:Fly.io(云主机托管,自带边缘网络与卷存储)
- 部署方式:本地
flyctl直接发布,无需额外 CI/CD 编排 - 运行方式:直接使用仓库内已有的 Dockerfile 与 fly.toml
- 数据持久化:Fly Volume 挂载到容器内
/data目录 - 访问地址:
https://omniroute.fly.dev/ - 应用名:
omniroute(本文所有命令均以该应用名为准)
覆盖三类场景:首次把当前项目部署到 Fly.io、后续代码更新继续发布、新项目参考同一流程部署。
2. 仓库现有配置解读:fly.toml 关键项
仓库根目录的fly.toml是本次部署的配置核心,已确认包含以下关键项:
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 应用,控制台看到的应用必须与之一致;destination = '/data'决定持久卷挂载目录,本项目必须让DATA_DIR=/data,否则数据库和密钥会写到容器临时目录,重启即丢失;[processes]中的node run-standalone.mjs是容器入口进程。
对比仓库中真实 fly.toml,还有几个文档之外的细节值得注意(均来自已验证配置):
- 卷自动扩容:
[[mounts]]段额外声明了auto_extend_size_threshold = 80、auto_extend_size_increment = '1GB'、auto_extend_size_limit = '10GB',即卷使用率达 80% 时自动按 1GB 步长扩容、上限 10GB; - HTTP 服务强化:
force_https = true强制 HTTPS;auto_stop_machines = 'stop'与auto_start_machines = true支持空闲停机、按需唤醒;min_machines_running = 1保证至少一台常驻; - 资源规格:
[[vm]]段为 1 核 shared CPU、1024MB 内存; - 部署钩子:
[deploy]中的release_command被注释掉,本部署不需要迁移命令。
在 Dockerfile 中可以看到与上述配置的对应关系:基础镜像为node:26-trixie-slim,ENV PORT=20128与internal_port一致,ENTRYPOINT先执行 check-permissions.sh(校验数据卷属主),随后CMD ["node", "dev/run-standalone.mjs"]启动服务;镜像内置HEALTHCHECK(每 30s 探测healthcheck.mjs)。注意 Dockerfile 中默认ENV DATA_DIR=/app/data,这正是 Fly 部署时必须用环境变量覆盖为/data的原因。
3. 环境准备:安装 Fly CLI 并登录
3.1 安装 Fly CLI
Windows PowerShell 一键安装:
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"如果安装脚本在当前环境失败,也可以手动下载flyctl二进制并加入PATH。
3.2 登录 Fly 账号
flyctl auth login3.3 检查登录状态
flyctl auth whoami flyctl versionwhoami输出当前登录账号,确认与目标组织一致后再继续。
4. 首次部署流程
4.1 获取代码并进入目录
git clone https://gitcode.com/GitHub_Trending/om/OmniRoute.git cd OmniRoute4.2 确认应用名
打开 fly.toml,重点看这一行:
app = 'omniroute'如果你要部署到自己的新应用,可改成全局唯一名称,例如:
app = 'omniroute-yourname'注意两点:
- 控制台里查看的应用必须与
fly.toml里app一致; - 如果以前用过别的名字(例如
oroute),不要和omniroute混淆——它们对应不同的 Fly 应用,Secrets 互不通用。
4.3 创建应用
如果该应用尚不存在:
flyctl apps create omniroute改名后请把omniroute替换成你的应用名。
4.4 首次部署
flyctl deployflyctl会依据fly.toml构建镜像并创建首批 Machine;若尚未配置 Secrets,可以先看下一节再部署,避免首次启动回退到弱默认值。
5. 必配参数:Fly Secrets 密钥体系
5.1 已验证使用的参数
以下参数已经在当前omniroute应用上实际部署验证:
API_KEY_SECRETDATA_DIRJWT_SECRETMACHINE_ID_SALTNEXT_PUBLIC_BASE_URLOMNIROUTE_WS_BRIDGE_SECRET(生产环境必需,用于 WebSocket bridge 鉴权)STORAGE_ENCRYPTION_KEY
5.2 关于INITIAL_PASSWORD
当前项目没有设置INITIAL_PASSWORD,因为本次部署按需求不使用它。如果不设置:
- 启动日志会提示默认密码为
CHANGEME(源码见 bootstrap-env.mjs 中的告警逻辑); - 部署后应尽快在系统设置中修改登录密码。
如果希望无人值守初始化后台密码,可后续补充INITIAL_PASSWORD。
5.3 源码视角:这些密钥为什么"必需"
理解这些密钥的底层作用,才能判断取舍。核心逻辑集中在 bootstrap-env.mjs,它由 run-standalone.mjs 在进程启动时调用:
- 密钥自动生成与持久化:首次启动时,若
JWT_SECRET、STORAGE_ENCRYPTION_KEY、API_KEY_SECRET缺失,bootstrap 会用randomBytes自动生成并写入{DATA_DIR}/server.env,因此只要DATA_DIR落在持久卷上,密钥就能跨重启、跨升级存活;这就是文档中/data/server.env成功标志的由来。 - 环境变量优先级:自动生成值 <
{DATA_DIR}/server.env< 首选.env文件 <process.env(Fly Secrets 注入的环境变量优先级最高),且空字符串值会被过滤,不会覆盖已持久化的真实密钥。 STORAGE_ENCRYPTION_KEY的约束:如果数据库中已存在enc:v1:前缀的加密凭据,bootstrap 会拒绝自动生成新密钥并报错,防止换钥匙导致数据不可解密;启动时还会用 scrypt 派生密钥做解密探测,密钥不匹配会给出告警。MACHINE_ID_SALT:在 machineId.ts 中,getConsistentMachineId以操作系统机器标识(Windows MachineGuid / macOS IOPlatformUUID / Linux machine-id 文件等)加盐后做 SHA-256,取前 16 字符作为稳定机器 ID;MACHINE_ID_SALT就是这里的盐,不设置时回退到内置默认值endpoint-proxy-salt。OMNIROUTE_WS_BRIDGE_SECRET:在 management.ts 中,进程内 codex Responses-over-WebSocket 代理的内部调用通过请求头x-omniroute-ws-bridge-secret携带该密钥,服务端以 SHA-256 摘要 +timingSafeEqual做常数时间比较;缺失该密钥会直接破坏 WebSocket bridge 握手,因此生产环境必配。NEXT_PUBLIC_BASE_URL:在 runtimeEnv.ts 中经 Zod 校验为合法 HTTP(S) URL,用于调度器、前端回调等场景的绝对地址生成。
6. 推荐参数说明
6.1 Secrets 设置建议
| 变量名 | 是否推荐 | 说明 |
|---|---|---|
API_KEY_SECRET | 必需 | API Key 生成与校验使用 |
JWT_SECRET | 必需 | 登录态和 JWT 签名使用 |
OMNIROUTE_WS_BRIDGE_SECRET | 生产必需 | WebSocket bridge 鉴权密钥 |
STORAGE_ENCRYPTION_KEY | 强烈推荐 | 加密存储敏感连接信息(AES-256-GCM) |
MACHINE_ID_SALT | 推荐 | 生成稳定机器标识 |
INITIAL_PASSWORD | 可选 | 首次部署时直接指定后台初始密码 |
| OAuth/API 私密凭证 | 按需 | 各类外部平台鉴权配置 |
6.2 当前项目推荐值
| 变量名 | 推荐值 |
|---|---|
DATA_DIR | /data |
NEXT_PUBLIC_BASE_URL | https://omniroute.fly.dev |
说明:
DATA_DIR=/data非常关键,必须与 fly.toml 中 Fly Volume 挂载点一致,否则数据库与密钥落入容器临时目录(默认/app/data);NEXT_PUBLIC_BASE_URL用于调度器和前端回调等场景,改动应用名时必须同步更新。
6.3 OAuth 回调地址配置(启用 OAuth Provider 时)
如果要在 Fly 部署上启用基于 OAuth 的 Provider(如 Antigravity、Gemini、Cursor 等),需确认两点:
将
NEXT_PUBLIC_BASE_URL设置为你的公网 HTTPS 域名flyctl secrets set NEXT_PUBLIC_BASE_URL=https://omniroute.fly.dev -a omniroute使用自定义域名时替换为对应域名(例如
https://omniroute.yourdomain.com)。在 Provider 控制台配置回调地址
所有 OAuth Provider共享唯一的回调路径
/callback,不存在按 Provider 区分的回调路由:<NEXT_PUBLIC_BASE_URL>/callback例如无论 Gemini、Antigravity、Cursor 还是 GitLab Duo,一律填写:
https://omniroute.fly.dev/callback
如果
NEXT_PUBLIC_BASE_URL与在 Provider 侧登记的回调地址不一致,OAuth 流程会在浏览器重定向环节失败。仓库中 OIDC 登录的回调实现可见 oidc/callback/route.ts:它校验code与state(常数时间比较防时序攻击)、从设置中读取 issuer/client 信息、基于x-forwarded-proto与Host动态拼出绝对redirect_uri并完成授权码换令牌,因此回调域名必须与NEXT_PUBLIC_BASE_URL严格对应。
7. 一键设置参数
下面命令会生成安全随机值,并把当前项目需要的参数一次性写入 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在 Linux / macOS 上,也可以用openssl rand -hex生成(适合 Shell 环境):
flyctl secrets set OMNIROUTE_WS_BRIDGE_SECRET=$(openssl rand -hex 32) -a omniroute注意:
OMNIROUTE_WS_BRIDGE_SECRET生产环境必配,缺失会破坏 WebSocket bridge 握手(见 5.3 节源码分析)。
如果你还要加初始密码:
flyctl secrets set INITIAL_PASSWORD=你的强密码 -a omniroute每次flyctl secrets set后 Fly 会自动滚动重启受影响 Machine。
8. 查看当前参数
flyctl secrets list -a omniroute如果控制台Secrets页面没有显示你期待的变量,先检查:
- 看的应用是不是
omniroute; fly.toml的app是否和控制台应用一致。
9. 后续更新发布
代码有更新后,发布步骤很简单:
git pull flyctl deploy如果只更新参数、不改代码:
flyctl secrets set KEY=value -a omnirouteFly 会自动滚动更新机器,期间服务基本不中断。
9.1 跟踪原仓库更新并保留 fork 的 fly.toml
如果当前仓库是 fork,且要同步上游更新,推荐按以下流程执行。先确认远程:
git remote -v应至少包含:origin指向你自己的 fork,upstream指向原仓库。如果没有upstream,先添加(将原仓库地址替换为你实际的上游):
git remote add upstream <原仓库地址>同步上游前,先抓取最新提交和标签:
git fetch upstream --tags查看当前版本和上游标签:
git describe --tags --always git show --no-patch --oneline <目标版本标签>说明:文档中
v3.4.7属于历史示例版本;实际发布时应使用当前版本标签(例如:v3.8.0)或:latest,以仓库实际版本为准。
如果你想合并上游最新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 自定义部署配置不被覆盖。
如果你明确只想对齐某个发布标签,也可以先确认标签是否已包含在upstream/main:
git merge-base --is-ancestor <目标版本标签> upstream/main返回成功表示upstream/main已包含该版本,直接合并upstream/main即可。
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说明站点已正常响应。
11. 成功标志
部署成功后,日志里应看到类似内容:
[bootstrap] Secrets persisted to: /data/server.env [DB] SQLite database ready: /data/storage.sqlite这两个点很关键:
/data/server.env说明运行时密钥落到了持久卷(由 bootstrap-env.mjs 写入,跨重启存活);/data/storage.sqlite说明 SQLite 数据库写入持久卷。
如果你看到的是/app/data/...,说明DATA_DIR没配对(回退到了 Dockerfile 中的默认值),需要立即修正为/data并重新部署。
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。
另外可核对日志中数据库路径是否为/data/storage.sqlite(见第 11 节)。
12.5 不设置 INITIAL_PASSWORD 是否能跑
可以运行,但会回退到默认CHANGEME密码。生产环境建议尽快在系统设置中修改后台密码。
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。
不要直接复用旧项目的密钥——STORAGE_ENCRYPTION_KEY与旧数据库绑定,换环境换库必须换钥匙;其他密钥复用也会放大泄露面。
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 omniroute(第 7 节一键命令)flyctl deployflyctl logs --no-tail -a omniroute
延伸阅读
- 部署指南英文原版:docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md
- 部署入口与密钥引导:run-standalone.mjs / bootstrap-env.mjs
- 容器镜像与启动命令:Dockerfile / docker-compose.yml
- 机器标识与 WS bridge 鉴权:machineId.ts / management.ts
- 运行时环境校验:runtimeEnv.ts
- 本地/自建部署对比参考:DOCKER_GUIDE.md、SETUP_GUIDE.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),仅供参考