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 部署文档(原文见 docs/i18n/id/docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md,英文原版见 docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md),系统讲解如何将 OmniRoute(单端点接入 350+ 提供方、1200+ 模型的统一 AI 网关)发布到 Fly.io。指南覆盖两大核心场景:项目首次部署、以及后续代码更新的滚动发布,同时给出可复用到新项目的完整部署流程参考。读完本文,你将掌握flyctl发布、Fly Volume 持久化挂载、Secrets 一次性配置、fork 仓库同步上游更新并保留自定义fly.toml,以及用启动日志验证部署成败的完整技能。
1. 部署目标与环境概览
当前仓库针对 Fly.io 的部署方案已实测验证,目标环境如下:
| 项目 | 值 |
|---|---|
| 平台 | Fly.io |
| 发布方式 | 本地使用flyctl直接发布 |
| 运行载体 | 仓库内已就绪的Dockerfile与 fly.toml |
| 数据持久化 | Fly Volume 挂载到/data |
| 访问地址 | https://omniroute.fly.dev/ |
这套方案的特点是"开箱即用":不需要额外编写 Docker 编排或构建脚本,仓库根目录的 fly.toml 与 Dockerfile 已经包含完整的发布配置,本地只需登录账号后执行flyctl deploy即可。
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 应用,flyctl系列命令与 Fly 控制台均以此名定位应用。实际仓库文件第一行还标注了它由oroute于 2026-03-20 生成,因此历史遗留应用中可能存在oroute与omniroute并存的情况,操作时务必区分。destination = '/data':Fly Volume 挂载到容器的/data目录,这是数据库与密钥的持久化落点。[processes] app = 'node run-standalone.mjs':应用入口。对应 scripts/dev/run-standalone.mjs,该脚本首先调用bootstrapEnv()完成密钥引导,再以转发信号的方式启动真正的服务进程;且优先选择带 WebSocket 包装的server-ws.mjs(具备可信对端 IP 戳记),缺失时才回退到裸的server.js。internal_port = 20128:容器内 HTTP 服务端口。与 Dockerfile 中ENV PORT=20128及EXPOSE 20128一致。
此外,仓库实际fly.toml还包含了指南之外但同样值得了解的增强配置(当前仓库即为佐证,见 fly.toml):
- Volume 自动扩容:
auto_extend_size_threshold = 80、auto_extend_size_increment = '1GB'、auto_extend_size_limit = '10GB',当卷使用率超过 80% 时自动扩容 1GB,上限 10GB; - HTTP 服务加固:
force_https = true、auto_stop_machines = 'stop'(无流量时停机省额度)、auto_start_machines = true、min_machines_running = 1; - VM 规格:
memory = '1gb'、cpu_kind = 'shared'、cpus = 1。
关键前提:本项目必须设置
DATA_DIR=/data。若缺失,数据库和密钥会被写入容器临时目录,容器重建即丢失全部数据(详见第 6、7 节)。
3. 前置工具准备
3.1 安装 Fly CLI(flyctl)
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 versionauth whoami应输出当前登录账号,version输出 flyctl 版本号,二者均正常即说明 CLI 环境就绪。
4. 首次部署完整流程
4.1 拉取代码并进入目录
git clone <OmniRoute 仓库地址> cd OmniRoute4.2 确认应用名称
打开fly.toml,关注这一行:
app = 'omniroute'若计划部署到自己新建的应用,应改为全局唯一的名字,例如:
app = 'omniroute-yourname'注意两点:
- 在 Fly 控制台确认你看到的应用与
fly.toml中app的值一致; - 若之前曾用过其他名字(如
oroute),不要与omniroute混淆,flyctl命令与 Secrets 均按app名定向。
4.3 创建应用(若尚不存在)
flyctl apps create omniroute如果你已经改名,把omniroute替换成你自己的应用名。
4.4 执行首次部署
flyctl deployflyctl 会自动读取仓库根目录的 Dockerfile 构建镜像并创建第一个 Machine。构建链路的关键环节在 Dockerfile 中有明确体现:采用npm ci --ignore-scripts缩减供应链攻击面,随后针对better-sqlite3直接调用 node-gyp 重建原生绑定并做内存库冒烟测试,最后以非 root 用户node(UID/GID 1000)运行,并通过ENTRYPOINT中的check-permissions.sh检查挂载卷的属主。
5. 必配参数清单
以下是本项目在 Fly.io 上运行的最低推荐参数集合。
5.1 已实测验证的参数
以下参数已在当前omniroute应用上真实使用:
API_KEY_SECRETDATA_DIRJWT_SECRETMACHINE_ID_SALTNEXT_PUBLIC_BASE_URLSTORAGE_ENCRYPTION_KEY
5.2 关于INITIAL_PASSWORD
当前项目部署未设置INITIAL_PASSWORD,因为本次部署场景不需要它。其影响如下:
- 未设置时,启动日志会提示默认密码为
CHANGEME; - 部署完成后,应尽快在系统设置中修改登录密码;
- 若希望首次部署时自动初始化管理面板密码,可后续补加
INITIAL_PASSWORD。
这一行为在仓库的引导逻辑中得到了源码级印证: scripts/build/bootstrap-env.mjs 会在INITIAL_PASSWORD缺失或等于CHANGEME时输出警告⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!。
6. 推荐参数逐项说明
6.1 建议存入 Fly Secrets 的参数
| 变量名 | 推荐程度 | 作用说明 |
|---|---|---|
API_KEY_SECRET | 必需 | 用于 API Key 的生成与校验签名 |
JWT_SECRET | 必需 | 用于登录会话与 JWT 签名 |
STORAGE_ENCRYPTION_KEY | 强烈推荐 | 加密敏感连接信息(provider 凭据字段) |
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 的挂载点destination = '/data'保持一致;NEXT_PUBLIC_BASE_URL用于调度器回调和前端回调等场景,需指向应用的公网地址。
源码级原理补充:DATA_DIR的解析逻辑定义在 src/lib/dataPaths.ts,优先读取process.env.DATA_DIR,未配置时才回退到默认目录(Linux 下为~/.omniroute,Windows 下为%APPDATA%\omniroute)。引导脚本 scripts/build/bootstrap-env.mjs 复刻了同样的解析顺序。而密钥的持久化在 scripts/build/bootstrap-env.mjs:首次启动时自动生成缺失的密钥并写入{DATA_DIR}/server.env,确保重启、卷重挂载与升级后密钥不丢失。若DATA_DIR指向容器临时目录,server.env与 SQLite 数据库都会随之蒸发。
7. 一次性设置全部参数
下面的 PowerShell 命令会生成安全随机值,并一步到位地把当前项目所需参数写入 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() 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=kata-sandi-kuat-anda -a omniroute随机值的位数与仓库引导逻辑的期望吻合:JWT_SECRET在缺失时由randomBytes(64)生成(128 位十六进制),STORAGE_ENCRYPTION_KEY与API_KEY_SECRET由randomBytes(32)生成(见 scripts/build/bootstrap-env.mjs)。即使不手动设置这些 Secret,首次启动也会自动生成并持久化;手动预置的意义在于部署前锁定密钥值,保证多实例/跨环境一致性。
顺序提示:
flyctl secrets set会触发 Machine 滚动重启,所以最佳实践是先设置 Secrets 再首次flyctl deploy,避免先启动后又重启。
8. 查看当前参数
flyctl secrets list -a omniroute如果 Fly 控制台的Secrets页面没有显示你预期的变量,请依次排查:
- 当前查看的应用是否为
omniroute; fly.toml中app的值是否与控制台中的应用一致。
9. 后续更新与滚动发布
9.1 常规代码更新
代码更新后的发布流程非常简单:
git pull flyctl deploy若只是更新参数而不改代码:
flyctl secrets set KEY=value -a omnirouteFly 会自动以滚动更新方式重启 Machine 使参数生效。
9.2 Fork 仓库同步上游更新并保留自定义fly.toml
如果当前仓库是 fork,且希望同步上游的更新,可以遵循以下流程。
先确认现有 remote:
git remote -v应至少包含:
origin:指向你自己的 fork;upstream:指向原仓库。
若没有upstream,先添加:
git remote add upstream <上游仓库地址>同步前先拉取上游最新的 commit 与 tag:
git fetch upstream --tags查看当前版本与上游 tag:
git describe --tags --always git show --no-patch --oneline v3.4.7若要合并最新upstream/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:从合并前的提交恢复你自己的fly.toml;- 若上游未改动
fly.toml,该步骤不会产生额外差异; - 若上游改动了
fly.toml,该步骤能确保 fork 自定义的部署配置(应用名、Volume 挂载、region 等)不被覆盖。
如果只想对齐某个特定发布 tag(如v3.4.7),先确认该 tag 已包含在upstream/main中:
git merge-base --is-ancestor v3.4.7 upstream/main命令成功(退出码 0)即说明upstream/main已包含该版本,可以直接合并upstream/main。
9.3 同步后的标准发布顺序
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即说明站点正常响应。注意请求经历了 HTTP→HTTPS 跳转(fly.toml中force_https = true),因此这里显式设置了-MaximumRedirection 5。
11. 部署成功的判定指标
部署成功后,启动日志中应当出现以下两行:
[bootstrap] Secrets persisted to: /data/server.env [DB] SQLite database ready: /data/storage.sqlite这两点至关重要:
/data/server.env:运行时密钥已持久化到卷中,说明DATA_DIR生效且卷挂载正确;/data/storage.sqlite:SQLite 数据库已写入持久卷,业务数据不会随容器销毁而丢失。
如果看到的是/app/data/...,说明DATA_DIR配置不正确,需要立即修复。
仓库源码与这两条日志一一对应:
Secrets persisted to: ...输出于 scripts/build/bootstrap-env.mjs,路径由join(dataDir, "server.env")计算,dataDir即DATA_DIR解析结果;[DB] SQLite database ready:输出于 src/lib/db/core.ts,其路径由path.join(DATA_DIR, "storage.sqlite")生成(见 src/lib/db/core.ts)。
值得说明的是:镜像内默认DATA_DIR=/app/data(Dockerfile),这正是"看到/app/data即配置错误"的根源——必须用 Fly Secrets 里的DATA_DIR=/data覆盖默认值,使其落到 Volume 挂载点。
12. 常见问题排查
12.1Secrets页面为空
通常有两种原因:
- 还没有执行过
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。
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; - 首次部署后检查日志确认数据已写入
/data。
切勿复用旧项目的密钥——STORAGE_ENCRYPTION_KEY一旦与已加密凭据不匹配,引导阶段会直接拒绝自动生成并提示恢复原密钥(见 scripts/build/bootstrap-env.mjs),且运行时解密探测会输出STORAGE_ENCRYPTION_KEY does not match的告警(见 scripts/build/bootstrap-env.mjs)。
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.io 部署配置文件(含 Volume 自动扩容与机器生命周期策略)
- Dockerfile —— 多阶段构建镜像(含 CVE 修补、原生模块重建、非 root 运行、健康检查)
- scripts/dev/run-standalone.mjs —— 运行时入口(引导密钥 + 启动 WebSocket 包装服务)
- scripts/build/bootstrap-env.mjs —— 零配置引导:密钥自动生成、
server.env持久化、密钥不匹配探测 - src/lib/dataPaths.ts ——
DATA_DIR解析与默认目录回退逻辑 - src/lib/db/core.ts —— SQLite 数据库初始化与就绪日志
- docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md —— 英文原版部署指南
- docs/i18n/zh-CN/docs/ops/FLY_IO_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),仅供参考