OmniRoute 部署到 Fly.io:fly.toml 配置、持久卷挂载与 Secrets 管理的完整实战指南
【免费下载链接】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/ops/FLY_IO_DEPLOYMENT_GUIDE.md 及其中文/葡语翻译版本),系统讲解如何把 OmniRoute 网关部署到 Fly.io:从fly.toml关键配置项、flyctl首次部署与后续发版流程,到DATA_DIR、JWT_SECRET、STORAGE_ENCRYPTION_KEY等运行时参数的设置原理。读完本文,你可以独立完成首次部署、理解每类参数的底层用途,并在代码更新或上游同步后安全地滚动发布。
1. 部署目标与运行形态
OmniRoute 在 Fly.io 上的部署采用"本地flyctl直接发布"的方式,复用仓库内现有的 Dockerfile 与 fly.toml,不依赖 Fly 的远程构建扩展:
| 项目 | 取值 |
|---|---|
| 平台 | Fly.io |
| 部署方式 | 本地flyctl deploy直接发布 |
| 运行方式 | 仓库内Dockerfile构建的独立(standalone)镜像 +fly.toml |
| 数据持久化 | Fly Volume 挂载到/data |
| 应用名 | omniroute |
| 访问地址 | https://omniroute.fly.dev/(以当前项目实测为准) |
运行入口由fly.toml的[processes]段指定:
[processes] app = 'node run-standalone.mjs'从源码结构看,这个入口在容器内执行 scripts/dev/run-standalone.mjs。它先调用bootstrapEnv()完成环境变量分层合并与密钥自举,再把PORT、堆大小等运行时参数注入子进程,最后优先选择带 WebSocket 支持的server-ws.mjs(用于授权中间件的 peer-IP 标记),否则回退到 Next.js standalone 的server.js。
2. 当前仓库fly.toml的关键配置
当前仓库中的 fly.toml 确认包含以下关键项(比部署文档中的摘录更完整):
app = 'omniroute' primary_region = 'sin' [build] [processes] app = 'node run-standalone.mjs' [[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" # Bind to all interfaces for Fly runtime networking. HOST = "0.0.0.0" HOSTNAME = "0.0.0.0" BIND = "0.0.0.0"各配置项的作用:
app = 'omniroute':决定部署到哪个 Fly 应用。控制台里查看的必须是对应的应用名,切勿与旧名(如oroute)混淆。primary_region = 'sin':主区域,新机器优先在该区域创建。[[mounts]]:destination = '/data'决定持久卷挂载目录。auto_extend_size_threshold = 80表示卷使用率达到 80% 时自动扩容,每次加1GB,上限10GB。本项目必须让DATA_DIR=/data,否则数据库和密钥会写到容器临时目录,重建后丢失。[http_service]的internal_port = 20128:与 Dockerfile 中ENV PORT=20128和EXPOSE 20128对应,Fly 边缘把 HTTPS 流量转发到容器内的该端口。auto_stop_machines/auto_start_machines/min_machines_running = 1:空闲停机但保留一台常驻机器的策略,兼顾成本与可用性。[[vm]]:1GB 内存、1 vCPU 共享型。从源码结构看,运行时堆大小由 Dockerfile 中的OMNIROUTE_MEMORY_MB=1024控制(--max-old-space-size),因此 1GB 机器规格与该默认值匹配。[env]中的HOST/HOSTNAME/BIND = "0.0.0.0":Fly 运行时网络要求进程绑定所有接口;TZ用于统一日志时区。
3. 必备工具:安装与登录 Fly CLI
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 version4. 首次部署当前项目
4.1 获取代码并进入目录
git clone <你的仓库地址> cd OmniRoute适用前提:本文中的部署流程针对 OmniRoute 仓库根目录,要求根目录存在
Dockerfile和fly.toml;flyctl deploy会在本地构建镜像后推送发布。
4.2 确认应用名
打开fly.toml,重点看这一行:
app = 'omniroute'如果你准备部署到自己的新应用,可改成全局唯一名称,例如:
app = 'omniroute-yourname'注意:控制台里要看的是与fly.toml里app一致的应用;如果以前用过别的名字(例如oroute),不要和omniroute混淆。
4.3 创建应用
如果该应用尚不存在:
flyctl apps create omniroute如果已改成别的应用名,把omniroute替换成你的名字。
4.4 首次部署
flyctl deploy5. 必配参数与运行时自举机制
本项目在 Fly.io 上建议至少配置以下参数,它们已经验证可用于当前omniroute应用:
API_KEY_SECRETDATA_DIRJWT_SECRETMACHINE_ID_SALTNEXT_PUBLIC_BASE_URLSTORAGE_ENCRYPTION_KEY
5.1 这些参数在代码中如何使用
仓库的启动链路对理解"为什么必须配置它们"很有帮助:
- 容器启动时,
run-standalone.mjs调用的bootstrapEnv()(实现在 scripts/build/bootstrap-env.mjs)会做三层合并:已持久化的<DATA_DIR>/server.env→ 项目.env→ 进程环境变量(空值会被过滤,避免docker run -e KEY=覆盖真实值)。 - 如果
JWT_SECRET、STORAGE_ENCRYPTION_KEY、API_KEY_SECRET缺失,自举逻辑会用randomBytes自动生成并写入<DATA_DIR>/server.env,日志输出[bootstrap] Secrets persisted to: /data/server.env。 - 关键约束:如果
<DATA_DIR>/storage.sqlite中已存在enc:v1:*加密凭证,而STORAGE_ENCRYPTION_KEY缺失,自举会直接抛错拒绝启动(防止新密钥无法解密旧凭证)。所以在 Fly 这类无状态重建频繁的环境中,把这三个密钥显式放入 Secrets 是更稳妥的做法,而不是依赖自动生成。 INITIAL_PASSWORD未设置时,自举会打印警告并回退到默认密码CHANGEME,部署后应尽快在系统设置中修改登录密码。
5.2 关于INITIAL_PASSWORD
当前项目没有设置INITIAL_PASSWORD,因为本次部署按需求不使用它。如果不设置:
- 启动日志会提示默认密码是
CHANGEME; - 部署后应尽快在系统设置中修改登录密码。
如果你希望无人值守初始化后台密码,也可以后续补:
flyctl secrets set INITIAL_PASSWORD=你的强密码 -a omniroute6. 推荐参数说明
6.1 Secrets 中设置
建议放入 Fly Secrets:
| 变量名 | 是否推荐 | 说明 |
|---|---|---|
API_KEY_SECRET | 必需 | API Key 生成与校验使用 |
JWT_SECRET | 必需 | 登录态和 JWT 签名使用 |
STORAGE_ENCRYPTION_KEY | 强烈推荐 | 加密存储敏感连接信息(provider_connections中的enc:v1:*字段) |
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]]的挂载点一致。注意 Dockerfile 中 runner 阶段的镜像默认值是ENV DATA_DIR=/app/data(配合docker-compose卷),部署到 Fly 时必须用环境变量覆盖为/data,这正是"数据没有持久化"这类问题的常见根因。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() 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从源码看各长度的对应关系:JWT_SECRET生成 64 字节十六进制(与自举中randomBytes(64)对齐),API_KEY_SECRET、STORAGE_ENCRYPTION_KEY、MACHINE_ID_SALT各 32 字节,与自举逻辑生成的长度一致,保证替换后兼容既有enc:v1:数据。
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指向你自己的 forkupstream指向原仓库
如果没有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.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说明站点已正常响应。此外,镜像内置健康检查(Dockerfile 中HEALTHCHECK调用node healthcheck.mjs,即 scripts/dev/healthcheck.mjs),可作为容器存活信号。
11. 成功标志:日志里验证持久化是否生效
部署成功后,日志里应看到类似内容:
[bootstrap] Secrets persisted to: /data/server.env [DB] SQLite database ready: /data/storage.sqlite这两行日志都能在源码中找到出处:
- 第一行由 scripts/build/bootstrap-env.mjs 在密钥写入
<DATA_DIR>/server.env后打印,说明运行时密钥落到了持久卷; - 第二行由 src/lib/db/core.ts 在打开数据库成功后打印(
[DB] SQLite database ready: ${sqliteFile}),说明数据库写入持久卷。
如果你看到的是/app/data/...,说明DATA_DIR没配对(回退到了 Dockerfile 的镜像默认值/app/data),需要立即通过flyctl secrets set DATA_DIR=/data -a omniroute修正并重新部署。
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(注意 Docker 镜像默认DATA_DIR=/app/data,必须显式覆盖)
另外,Dockerfile 的ENTRYPOINT是 scripts/check-permissions.sh,会在挂载卷属主不正确时发出警告,检查日志时也可留意该输出。
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:一旦storage.sqlite中存在enc:v1:*凭证,换用新密钥将导致这些数据永久无法解密(自举逻辑会拒绝在新密钥下继续运行),迁移时必须沿用原密钥。
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/pt-BR/docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md)。如需了解 SQLite 运行时细节与数据目录约定,可参考 docs/ops/SQLITE_RUNTIME.md;容器本地部署方式见 docs/guides/DOCKER_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),仅供参考