OmniRoute Fly.io 部署实战指南:flyctl 发布流程、Secrets 配置与 /data 持久化
2026/9/12 10:07:02 网站建设 项目流程

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+ 模型),其仓库内自带了可直接上云的Dockerfilefly.toml。本文以仓库中已验证的omniroute应用配置为蓝本,完整讲解如何用本地flyctl把当前项目发布到 Fly.io:从工具安装、首次部署、Secrets 参数体系,到 fork 同步上游后的增量发布、发布后检查与故障排查,帮助你在半小时内跑通"一次部署、长期可升级"的生产环境。


1. 部署目标与整体方案

本文记录的部署方案基于仓库中已验证通过的配置,核心目标如下:

  • 平台:Fly.io
  • 部署方式:本地flyctl直接发布,不依赖 CI 流水线
  • 运行方式:复用仓库内现有的Dockerfilefly.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 20128ENV 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

理解启动链路有助于排查部署问题。容器启动后按如下顺序执行:

  1. Dockerfile 的ENTRYPOINT执行check-permissions.sh(校验挂载卷属主,防止非 root 用户无法写入/data);
  2. CMD执行node dev/run-standalone.mjs,即仓库 scripts/dev/run-standalone.mjs;
  3. 该脚本首先调用bootstrapEnv(),随后拉起server-ws.mjs(带 WebSocket 桥接包装的 Next.js standalone 服务)或回退到server.js

bootstrapEnv()的实现位于 scripts/build/bootstrap-env.mjs,它是"密钥持久化"的关键:

  • JWT_SECRETSTORAGE_ENCRYPTION_KEYAPI_KEY_SECRET缺失,会自动生成安全随机值;
  • 生成的密钥会写入{DATA_DIR}/server.env,日志输出[bootstrap] Secrets persisted to: <路径>
  • 环境变量优先级从低到高为:自动生成默认值 →{DATA_DIR}/server.env(首次启动持久化)→ 偏好.envDATA_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 login

4.3 检查登录状态

flyctl auth whoami flyctl version

whoami输出应显示你的 Fly 账号邮箱,version输出flyctl版本号。


5. 首次部署当前项目

5.1 获取代码并进入目录

git clone https://gitcode.com/GitHub_Trending/om/OmniRoute.git cd OmniRoute

5.2 确认应用名

打开 fly.toml,重点确认这一行:

app = 'omniroute'

如果你准备部署到自己的新应用,可改成全局唯一名称,例如:

app = 'omniroute-yourname'

注意:Fly 控制台里查看的应用必须与fly.toml中的app一致;如果之前用过其他名字(例如oroute),不要与omniroute混淆。

5.3 创建应用

如果该应用尚不存在:

flyctl apps create omniroute

若你已改名,把omniroute替换成你的应用名。

5.4 首次部署

flyctl deploy

flyctl会依据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— 数据目录,必须为/data
  • JWT_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 中getConsistentMachineIdsalt || 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_SECRETQODER_OAUTH_CLIENT_SECRET等可选 OAuth 密钥

6.3 当前项目推荐值

变量名推荐值
DATA_DIR/data
NEXT_PUBLIC_BASE_URLhttps://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 omniroute

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

说明:

  • 各密钥的长度选择与 bootstrap-env.mjs 的自动生成规格一致(API_KEY_SECRETSTORAGE_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 omniroute

8. 查看当前参数

flyctl secrets list -a omniroute

如果 Fly 控制台的Secrets页面没有显示你期待的变量,先检查两点:

  • 看的应用是不是omniroute(而不是旧的oroute等其他应用);
  • fly.toml里的app是否和控制台应用一致。

9. 后续更新发布

代码有更新后,发布步骤很简单:

git pull flyctl deploy

如果只更新参数、不改代码:

flyctl secrets set KEY=value -a omniroute

Fly 会自动对机器执行滚动更新。

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 同步上游后的标准发布顺序

同步原仓库完成后,推荐按下面顺序发布:

  1. git fetch upstream --tags
  2. git merge upstream/main
  3. 恢复 fork 的fly.toml
  4. git push origin main
  5. flyctl deploy
  6. flyctl status -a omniroute
  7. flyctl logs --no-tail -a omniroute

这就是当前项目升级时使用的实际流程。


10. 发布后检查

10.1 查看应用状态

flyctl status -a omniroute

10.2 查看启动日志

flyctl logs --no-tail -a omniroute

10.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_DIRSQLITE_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 omniroute

12.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. 新项目复用建议

如果以后是新项目照着这份文档部署,最少改这几项:

  1. 修改 fly.toml 里的app
  2. 修改NEXT_PUBLIC_BASE_URL为你的公网域名;
  3. 保持DATA_DIR=/data,与卷挂载点严格一致;
  4. 重新生成API_KEY_SECRETJWT_SECRETMACHINE_ID_SALTSTORAGE_ENCRYPTION_KEY(生产环境再加OMNIROUTE_WS_BRIDGE_SECRET);
  5. 首次部署后检查日志是否写入/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

如果是新环境首次部署,核心步骤就是:

  1. flyctl auth login
  2. flyctl apps create omniroute
  3. flyctl secrets set ... -a omniroute
  4. flyctl deploy
  5. flyctl 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询