使用 Docker Compose 自托管 Maybe:从安装、安全配置到镜像更新的完整部署指南
本篇技术指南基于 Maybe 仓库的官方自托管文档 docs/hosting/docker.md 展开,覆盖用 Docker Compose 部署 Maybe 的完整流程:安装 Docker、获取并理解 Compose 配置、生成 SECRET_KEY_BASE 等安全环境变量、以前台/后台模式运行应用,以及基于 GHCR 镜像的日常更新与版本固定。读完后你可以独立搭建、维护一台自托管的 Maybe 实例,并能看懂仓库中 compose.example.yml 与 Dockerfile 的关键实现细节。
一、前置准备:安装并验证 Docker
自托管 Maybe 最推荐的方式是 Docker Compose,它负责一次性拉起应用、数据库和缓存三个服务。准备工作分三步:
- 按 Docker 官方安装指南安装 Docker Engine(支持 Linux、macOS 等平台);
- 启动本机的 Docker 服务;
- 在终端运行验证命令,确认 Docker 已正确安装并处于运行状态:
# 如果 Docker 配置正确,该命令会成功执行
docker run hello-world
该命令会从镜像仓库拉取一个最小镜像并运行;只要能看到成功输出,说明后续步骤所需的环境已就绪。
二、创建部署目录并获取 Compose 配置文件
首先为应用创建一个专属运行目录(目录名可自定,下例采用推荐命名):
# 在电脑上创建存放 Docker 文件的目录(名称可任意)
mkdir -p ~/docker-apps/maybe
# 将当前工作目录切换到新文件夹
cd ~/docker-apps/maybe
然后在当前目录下下载仓库提供的示例 Compose 文件并另存为 compose.yml:
# 从 Maybe 的 GitHub 仓库下载示例 compose 文件
curl -o compose.yml https://raw.githubusercontent.com/maybe-finance/maybe/main/compose.example.yml
这条命令做了两件事:
- 从 Maybe 的公开仓库拉取示例 Docker Compose 文件(即仓库中的 compose.example.yml);
- 在当前目录生成一个名为
compose.yml的文件,内容即该示例文件。
执行完毕后,当前目录下应该只有 compose.yml 一个文件。你也可以跳过下载,直接从仓库中阅读 compose.example.yml 的内容并自行保存为 compose.yml。
三、读懂 compose.example.yml:四个服务与关键环境变量
compose.example.yml 是一个开箱即用的标准配置,但如果你想了解它实际做了什么,可以对照仓库源文件逐段阅读。该文件由四个服务、三个卷(volume)和一个网络组成:
| 服务 | 镜像 | 作用 |
|---|---|---|
web |
ghcr.io/maybe-finance/maybe:latest |
Rails 主应用,对外暴露 3000 端口 |
worker |
同上,command: bundle exec sidekiq |
Sidekiq 后台任务进程 |
db |
postgres:16 |
PostgreSQL 数据库,数据挂载到 postgres-data 卷 |
redis |
redis:latest |
Redis 缓存/队列,数据挂载到 redis-data 卷 |
几个值得注意的设计(均来自 compose.example.yml 原文):
- 环境变量复用:文件顶部用 YAML anchor 定义了
x-db-env(POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB)和x-rails-env(在 db 变量基础上追加应用变量),四个服务通过锚点引用,保证配置一致。 - 带默认值的变量:所有变量都提供了默认值,例如
POSTGRES_USER默认maybe_user、POSTGRES_PASSWORD默认maybe_password、POSTGRES_DB默认maybe_production——这正是文档说"默认零配置即可运行"的原因。这也解释了为什么应用侧 config/database.yml 中 production 环境通过ENV.fetch("POSTGRES_DB") { "maybe_production" }读取库名,并通过DB_HOST/DB_PORT定位数据库(Compose 中固定为DB_HOST: db、DB_PORT: 5432)。 - 健康检查与依赖顺序:
db用pg_isready做 healthcheck,redis用redis-cli ping做 healthcheck;web服务声明depends_on且condition: service_healthy,确保数据库就绪后才启动应用。 SELF_HOSTED: "true":应用通过该变量识别自身处于自托管模式。从源码结构看,config/application.rb 中app_mode正是在SELF_HOSTED == "true"时取值为self_hosted,否则为managed,该模式会影响应用内展示的行为(例如托管模式下的升级提示)。REDIS_URL: redis://redis:6379/1:Sidekiq 队列连接 Redis 的 1 号库。后台任务的队列配置见 config/sidekiq.yml,包含scheduled、high_priority、medium_priority、low_priority、default五条队列,并发数由RAILS_MAX_THREADS(默认 3)决定——worker服务正是承载这些任务的进程。- 数据卷:
app-storage挂载到容器内/rails/storage(对应 Dockerfile 中预置的storage目录),postgres-data与redis-data分别保存数据库与缓存数据。三个卷都是持久化更新的关键:更新镜像时数据不会丢失。 - 可选的
OPENAI_ACCESS_TOKEN:文件中以注释明确提醒,启用后使用 AI 相关功能(聊天、规则)会产生费用,建议先在账户中设置好消费上限再配置。
四、(可选)创建 .env 文件加强安全性
compose.example.yml 默认无需任何配置即可运行。但如果你要把实例暴露在局域网之外的公网环境,建议按文档补充环境变量以增强安全性;仅本机使用且不在意安全性的话可以跳过本节。
1. 创建 .env 文件
Docker 从名为 .env 的文件读取环境变量,用以下命令创建空文件:
touch .env
2. 生成 SECRET_KEY_BASE
应用运行依赖环境变量 SECRET_KEY_BASE。安装 openssl 的话可以直接生成:
openssl rand -hex 64
如果没有 openssl,也可以用这条零依赖的 bash 命令生成同样长度的十六进制密钥:
head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n' && echo
生成后妥善保存该密钥,进入下一步。
3. 填写 .env 文件
用任意文本编辑器打开刚才创建的 .env,填入以下变量:
SECRET_KEY_BASE="replacemewiththegeneratedstringfromthepriorstep"
POSTGRES_PASSWORD="replacemewithyourdesireddatabasepassword"
其中 SECRET_KEY_BASE 替换为上一步生成的字符串,POSTGRES_PASSWORD 替换为你想要的数据库密码。这两个变量会分别覆盖 compose.example.yml 中 x-rails-env 与 x-db-env 里对应的默认值。
五、启动应用并创建账号
一切就绪,先用前台模式启动,便于观察日志、确认无误:
docker compose up
该命令会拉取官方 Docker 镜像并启动全部服务,终端中会滚动输出日志。随后打开浏览器访问 http://localhost:3000,如果一切正常,你会看到 Maybe 的登录页。
这里的"首次启动即自动建库"并非魔法。镜像的入口脚本是 bin/docker-entrypoint,其逻辑只有两行核心判断:当被执行的命令是 ./bin/rails server 时,先执行 ./bin/rails db:prepare(创建或迁移数据库),再 exec 启动原始命令。而 Dockerfile 正是将 ENTRYPOINT 指向该脚本、CMD 默认为 ./bin/rails server、EXPOSE 3000,与 Compose 文件中 3000:3000 的端口映射对应。
首次运行时需要注册账号:在登录页点击 "create your account",填入邮箱和密码即可。
六、后台运行与状态检查
自托管用户通常希望 Maybe 常驻后台。按 Ctrl+C 停止前台进程后执行:
docker compose up -d
-d 参数让 Docker Compose 以 detached(分离)模式运行。可用以下命令验证其处于运行状态:
docker compose ls
此后实例即可持续通过 http://localhost:3000 访问。由于三个卷(app-storage、postgres-data、redis-data)都是具名卷,容器重启不会丢失账号、账户数据与交易记录。
七、更新自托管应用:GHCR 镜像与更新流程
更新自托管 Maybe 的核心机制是 compose.yml 中引用的 GHCR(GitHub Container Registry)镜像:
image: ghcr.io/maybe-finance/maybe:latest
官方推荐两种镜像 tag,也可以自行固定到任意版本:
ghcr.io/maybe-finance/maybe:latest——对应 main 分支最新提交;ghcr.io/maybe-finance/maybe:stable——对应最新发布版本。
从发布流水线的源码可以印证这两个 tag 的生成规则:.github/workflows/publish.yml 中,推送 main 分支时打 latest 标签,推送 v* 开头的版本 tag 时打 stable 标签;且镜像为 linux/amd64,linux/arm64 双架构构建(第 79 行),因此 x86 与 ARM(如 Apple Silicon、树莓派类设备)宿主机均可直接拉取。
需要强调的是:默认情况下你的应用不会自动更新。手动更新时,在部署目录中依次执行:
cd ~/docker-apps/maybe # 进入当初配置应用的目录
docker compose pull # 从 GHCR 拉取最新发布的镜像
docker compose build # 基于更新后的镜像重建应用
docker compose up --no-deps -d web worker # 用最新版本重启应用服务
其中 --no-deps 表示不连带重启 db、redis 等依赖服务,只重启 web 与 worker 两个应用服务,避免无谓地触碰数据库容器。
八、固定版本(版本回退/锁定)
如果希望把应用锁定在特定版本或 tag 上,只需编辑 compose.yml:
image: ghcr.io/maybe-finance/maybe:stable
修改后务必重启应用使新镜像生效:
docker compose pull # 从 GHCR 拉取镜像
docker compose build # 重建应用
docker compose up --no-deps -d web worker # 用最新版本重启
注意:compose.example.yml 中 web 与 worker 两个服务需要分别设置 image 字段,锁定版本时两处保持一致即可。
九、故障排查:ActiveRecord::DatabaseConnectionError
如果第一次启动 Maybe 时遇到数据库连接问题(ActiveRecord::DatabaseConnectionError),很可能是 Docker 之前已经用一个不同的默认角色初始化过 Postgres 数据库(通常源于先前某次启动尝试的残留数据)。
此时可以选择重置数据库。
注意:该操作会删除 Maybe 数据库中已有的所有数据,请谨慎执行。 对只是想把应用跑起来的初次使用者,一般可以安全地执行以下命令:
docker compose down
docker volume rm maybe_postgres-data # 数据库挂载的卷名
docker compose up
docker exec -it maybe-postgres-1 psql -U maybe -d maybe_production -c "SELECT 1;" # 验证问题已修复
执行上述命令后,原有的 Maybe 数据库会被删除并重新初始化。补充一点细节:验证命令中的用户名需与实际初始化角色一致——示例 Compose 中 POSTGRES_USER 的默认值是 maybe_user(见 compose.example.yml 的 x-db-env 锚点),如果你在 .env 中未改过它,验证时应使用 psql -U maybe_user -d maybe_production,数据库容器名也可能因 compose 项目名不同而有所变化,可先用 docker compose ps 查看实际容器名。
十、延伸阅读:镜像构建与部署相关文件
本文以部署文档为主线,以下仓库文件是理解其背后的关键依据,建议进一步阅读:
- docs/hosting/docker.md:本文对应的原始自托管文档;
- compose.example.yml:官方示例 Compose 配置,包含服务、健康检查、卷与网络定义;
- Dockerfile:多阶段构建镜像——基于 Ruby 3.4.4-slim 的基础镜像(第 4-5 行),构建阶段用
SECRET_KEY_BASE_DUMMY=1预编译资产(第 43 行),最终阶段以非 root 用户(uid 1000)运行(第 55-59 行); - bin/docker-entrypoint:入口脚本,负责启动 web 前自动执行
db:prepare; - .github/workflows/publish.yml:
latest/stable镜像标签的构建与发布流水线; - CONTRIBUTING.md:发现 bug 或有功能需求时,可参考仓库内的贡献指南提交反馈。
按照本文流程,你可以完成 Maybe 自托管实例的完整生命周期管理:安装依赖、生成密钥、启动与验证、后台常驻、拉取更新以及故障时的数据库重置。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00