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 统一 AI 网关在 Fly.io 平台上的真实部署流程展开,覆盖首次部署、密钥/环境变量配置、后续更新发布、fork 同步与发布后验证五个环节,并深入对应源码解释DATA_DIR、Fly Volume 与运行时密钥持久化的底层机制。读完本文,你将能独立把一个 OmniRoute 实例部署到omniroute.fly.dev,正确配置API_KEY_SECRET、JWT_SECRET、STORAGE_ENCRYPTION_KEY等核心参数,并通过日志判断数据库与密钥是否真正写入持久卷。
1. 部署目标与整体形态
OmniRoute 在 Fly.io 上的部署方式为:本地flyctl直接发布,复用仓库内现成的 fly.toml 与 Dockerfile,数据通过 Fly Volume 持久化到容器的/data目录,对外访问地址为https://omniroute.fly.dev/。
部署目标可归纳为:
| 项目 | 取值 |
|---|---|
| 平台 | Fly.io |
| 发布方式 | 本地flyctl直接部署 |
| 构建依据 | 仓库内Dockerfile+fly.toml |
| 数据持久化 | Fly Volume 挂载到容器/data |
| 访问地址 | https://omniroute.fly.dev/ |
| 应用名 | omniroute |
整个流程适用三类场景:首次把当前项目部署到 Fly.io、后续代码更新后继续发布、以及新项目参考同样的流程复用部署。
2. 仓库关键配置解析(fly.toml)
仓库根目录的 fly.toml 已包含可实际部署的完整配置,核心内容如下:
app = 'omniroute' primary_region = 'sin' [build] [processes] app = 'node run-standalone.mjs' [deploy] # release_command = "node ./dbsetup.js" [[mounts]] source = 'data' destination = '/data' auto_extend_size_threshold = 80 auto_extend_size_increment = '1GB' auto_extend_size_limit = '10GB' [http_service] internal_port = 20128 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 memory_mb = 1024 [env] TZ = "Asia/Shanghai" HOST = "0.0.0.0" HOSTNAME = "0.0.0.0" BIND = "0.0.0.0"各关键项的作用如下:
app = 'omniroute':决定实际部署到哪个 Fly 应用。若部署到自己的新应用,需改为全局唯一名称,例如omniroute-yourname。primary_region = 'sin':指定主部署区域(新加坡),Fly 会按该区域就近调度机器。[[mounts]]:source = 'data'是 Fly 侧的 Volume 名称,destination = '/data'决定持久卷在容器内的挂载目录;auto_extend_size_*三项控制卷容量达到阈值后自动扩容(80% 阈值、每次 +1GB、上限 10GB)。[processes] app = 'node run-standalone.mjs':指定应用主进程的启动命令。Fly 的[processes]表为每个命名进程单独调度机器,app进程对应的命令与 Dockerfile 末尾的CMD ["node", "dev/run-standalone.mjs"]语义一致,即以独立部署产物方式启动服务器。[http_service] internal_port = 20128:Fly 负载均衡器把外部流量转发到容器内的 20128 端口,该端口与 Dockerfile 中的ENV PORT=20128、EXPOSE 20128一一对应。[vm]:机器规格为共享 CPU、1 核、1GB 内存。需要注意的是 Dockerfile 中运行时堆上限默认OMNIROUTE_MEMORY_MB=1024,两者需配套;若你在fusionTuning.maxPanel等配置上做了大并行度调优,需同步通过-e OMNIROUTE_MEMORY_MB=2048提高堆上限,否则可能出现内存压力。[env]:TZ=Asia/Shanghai设置容器时区;HOST、HOSTNAME、BIND全部绑定0.0.0.0,是为了适配 Fly 运行时网络对全接口监听的硬性要求。
2.1 为什么DATA_DIR=/data是部署成败的关键
fly.toml中并没有直接写DATA_DIR,但这恰恰是需要重点注意的地方。src/lib/dataPaths.ts 中的resolveDataDir的解析顺序是:
- 若显式配置了
DATA_DIR,直接使用该路径; - 否则回退到默认用户目录(Linux 上为
~/.omniroute)。
而容器内默认用户目录是/home/node,属于容器临时文件系统。因此本项目在 Fly.io 上必须通过 Secrets 显式设置DATA_DIR=/data,否则数据库与密钥会写进容器临时目录,机器重建后数据全部丢失。这也是 fly.toml 中 Volume 挂载点选择/data的根本原因——保证挂载点与运行时数据目录完全一致。
3. 必备工具准备
3.1 安装 Fly CLI
Windows PowerShell 下执行官方安装脚本:
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"如果安装脚本在当前环境失败,也可以手动下载flyctl二进制并放入PATH。
3.2 登录与校验
flyctl auth login flyctl auth whoami flyctl versionauth login会在浏览器中完成 OAuth 授权;whoami用于确认当前登录账号,version确认 CLI 版本正常。发布前先跑这两条可以提前暴露凭证问题。
4. 首次部署流程
4.1 获取代码
git clone https://github.com/diegosouzapw/OmniRoute.git cd OmniRoute4.2 确认应用名
打开 fly.toml,确认app = 'omniroute'。如果你要部署到自己的新应用,改成全局唯一名称:
app = 'omniroute-yourname'注意区分控制台里的应用名:以前如果用过别的名字(例如oroute),不要和omniroute混淆,后续所有命令都以fly.toml中的app为准。
4.3 创建应用(如尚不存在)
flyctl apps create omniroute如果改了应用名,把omniroute替换成你的名字。
4.4 首次部署
flyctl deployFly 会自动读取fly.toml、按Dockerfile构建镜像、创建并挂载data卷、调度机器并配置负载均衡。
5. 必配参数与源码依据
在 Fly.io 上部署 OmniRoute 至少要配置以下环境变量,它们的用途都能在源码中找到明确实现。
5.1 已验证使用的参数
当前omniroute应用实际部署并验证的参数包括:
API_KEY_SECRETDATA_DIRJWT_SECRETMACHINE_ID_SALTNEXT_PUBLIC_BASE_URLSTORAGE_ENCRYPTION_KEY
各参数的源码依据:
API_KEY_SECRET(必需):用于 API Key 的生成与校验。src/shared/utils/apiKey.ts 中,若未设置该变量会输出[SECURITY] API_KEY_SECRET is not set. API key CRC validation is disabled.,即 API Key 的 CRC 校验被禁用;src/instrumentation-node.ts 会在启动时自动生成并持久化 64 位十六进制随机值。JWT_SECRET(必需):用于登录态与 JWT 签名。src/instrumentation-node.ts 显示,缺失时系统会自动生成 48 字节随机值并写入持久化存储。STORAGE_ENCRYPTION_KEY(强烈推荐):用于加密存储供应商连接等敏感信息。src/lib/db/encryption.ts 明确说明:未设置时进入 passthrough 模式,凭据以明文写入 SQLite(日志提示[Encryption] STORAGE_ENCRYPTION_KEY not set. Storing plaintext (passthrough mode).)。生产环境必须设置,且更换密钥会导致既有密文无法解密(仓库中对应错误提示为 "Re-authenticate this account, or verify STORAGE_ENCRYPTION_KEY matches the key used to store it.")。MACHINE_ID_SALT(推荐):用于生成稳定机器标识。src/shared/utils/machineId.ts 中getConsistentMachineId读取该盐值(默认endpoint-proxy-salt),对操作系统机器 ID 做 SHA-256 后取前 16 位作为稳定标识。在 Fly 这种按需重建机器的环境中,显式设置盐值可以保证标识稳定可复现。INITIAL_PASSWORD(可选):首次部署时直接指定后台初始密码,详见 5.2。
5.2 关于INITIAL_PASSWORD
当前项目没有设置INITIAL_PASSWORD。如果不设置:
- 启动日志会提示默认密码为
CHANGEME,对应 scripts/build/bootstrap-env.mjs 中的警告逻辑:⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!; - 部署后应尽快在系统设置中修改登录密码。
如果你希望无人值守初始化后台密码,可以补充设置INITIAL_PASSWORD。
6. 推荐参数总表
6.1 建议放入 Fly Secrets 的参数
| 变量名 | 是否推荐 | 说明 |
|---|---|---|
API_KEY_SECRET | 必需 | API Key 生成与校验使用 |
JWT_SECRET | 必需 | 登录态和 JWT 签名使用 |
STORAGE_ENCRYPTION_KEY | 强烈推荐 | 加密存储敏感连接信息 |
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 中[[mounts]] destination = '/data'的挂载点一致;NEXT_PUBLIC_BASE_URL用于调度器回调、前端回调等场景,须填写最终对外可访问的公网地址。
7. 一键设置参数(PowerShell)
下面的 PowerShell 脚本会生成安全的随机值,并把当前项目需要的参数一次性写入 Fly Secrets(不包含INITIAL_PASSWORD):
$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() flyctl secrets set ` API_KEY_SECRET=$apiKeySecret ` JWT_SECRET=$jwtSecret ` MACHINE_ID_SALT=$machineIdSalt ` STORAGE_ENCRYPTION_KEY=$storageKey ` DATA_DIR=/data ` NEXT_PUBLIC_BASE_URL=https://omniroute.fly.dev ` -a omniroute如果你还要加初始密码:
flyctl secrets set INITIAL_PASSWORD=你的强密码 -a omniroute脚本中各随机值的生成长度与源码中的预期一致:API_KEY_SECRET与STORAGE_ENCRYPTION_KEY为 32 字节(scripts/build/bootstrap-env.mjs 中randomBytes(32).toString("hex")生成 64 位十六进制),JWT_SECRET为 64 字节。也可以使用openssl rand -hex 32等工具替代生成。
7.1 一键设置背后的持久化机制
设置完成后,容器首次启动时 scripts/build/bootstrap-env.mjs 的bootstrapEnv会按三层优先级合并环境变量:持久化的server.env< 项目.env< 进程环境变量(含 Fly Secrets)。由于 Fly Secrets 会作为容器环境变量注入,因此优先级最高,能正确覆盖缺省值。
同时该函数还承担两件关键工作:
- 自动补齐缺失密钥:当
JWT_SECRET、STORAGE_ENCRYPTION_KEY、API_KEY_SECRET任一为空时自动生成随机值,并写入{DATA_DIR}/server.env(对应日志📁 Secrets persisted to: ...); - 密钥一致性探测:若数据库中已存在
enc:v1:前缀的加密凭据(provider_connections表的api_key/access_token等字段),会校验当前STORAGE_ENCRYPTION_KEY是否能正确解密,防止误用新密钥导致既有数据无法读取。
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,需要同步上游https://github.com/diegosouzapw/OmniRoute的更新,推荐流程如下。
先确认远程:
git remote -v应至少包含origin(指向自己的 fork)和upstream(指向原仓库)。如果没有upstream,先添加:
git remote add upstream https://github.com/diegosouzapw/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 应用名、挂载卷、区域等 fork 自定义部署配置不被覆盖。
如果你明确只想对齐某个发布标签(例如v3.4.7),可以先确认该标签是否已经包含在upstream/main中:
git merge-base --is-ancestor v3.4.7 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
这是当前项目升级到v3.4.7时使用的实际流程。
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这两行的含义都可以在源码中得到印证:
Secrets persisted to: /data/server.env:来自 scripts/build/bootstrap-env.mjs,表示自动生成的运行时密钥已写入持久卷下的server.env;SQLite database ready: /data/storage.sqlite:来自 src/lib/db/core.ts,日志会同时打印DATA_DIR=与SQLITE_FILE=的绝对路径,用于直接核对多副本/卷拓扑是否一致(同一问题在 src/app/api/storage/health/route.ts 的健康检查接口中也会以{DATA_DIR}/storage.sqlite形式暴露)。
如果你看到的是/app/data/...,说明DATA_DIR没配对,需要立即修正。这正是 Dockerfile 中ENV DATA_DIR=/app/data默认值与 Fly Volume 挂载点/data不一致导致的典型症状:Docker 本地部署默认数据目录是/app/data,而 Fly 上必须显式覆盖为/data才能命中持久卷。
另外,Dockerfile 内置的健康检查(HEALTHCHECK调用 scripts/dev/healthcheck.mjs 探测轻量/healthz端点)与check-permissions.sh入口脚本会在容器启动时检查数据卷属主,遇到权限问题会在日志中提前暴露。
12. 常见问题排查
12.1 Secrets 页面是空的
通常有两种原因:
- 还没执行
flyctl secrets set; - 打开的是另一个应用(例如
oroute),不是omniroute。
12.2flyctl deploy报app not found
先创建应用:
flyctl apps create omniroute12.3fly.toml解析失败
重点检查:
- 注释里是否有乱码字符;
- TOML 引号和缩进是否正确。
12.4 数据没有持久化
检查以下两点:
- fly.toml 中是否存在
destination = '/data'; DATA_DIR是否设置为/data。
二者缺一不可:Volume 挂载点与运行时数据目录必须指向同一路径,否则会出现"容器重启后数据丢失"或"多副本各写各的库"的假象。
12.5 不设置INITIAL_PASSWORD是否能跑
可以运行,但会回退到默认密码CHANGEME(对应 scripts/build/bootstrap-env.mjs 的启动警告)。生产环境建议尽快在系统设置中修改后台密码。
13. 新项目复用建议
如果以后新项目照着这份文档部署,最少要改这几项:
- 修改 fly.toml 里的
app; - 修改
NEXT_PUBLIC_BASE_URL; - 保持
DATA_DIR=/data; - 重新生成
API_KEY_SECRET、JWT_SECRET、MACHINE_ID_SALT、STORAGE_ENCRYPTION_KEY; - 首次部署后检查日志是否写入
/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 omnirouteflyctl deployflyctl logs --no-tail -a omniroute
延伸阅读
- fly.toml:Fly 平台部署配置(应用名、区域、卷、端口、进程)
- Dockerfile:多阶段镜像构建、运行时环境变量与非 root 运行
- src/lib/dataPaths.ts:
DATA_DIR解析与写入校验逻辑 - scripts/build/bootstrap-env.mjs:
server.env持久化与密钥自动生成 - src/lib/db/core.ts:SQLite 初始化与启动日志
- src/lib/db/encryption.ts:
STORAGE_ENCRYPTION_KEY加解密与 passthrough 模式 - src/shared/utils/machineId.ts:
MACHINE_ID_SALT稳定机器标识实现 - docs/ops/MONITORING_GUIDE.md 与 docs/ops/VM_DEPLOYMENT_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),仅供参考