在 Coolify 上部署

使用 Coolify、已发布的 Docker 镜像、持久化卷和 HTTPS,在 VPS 上运行 Warmbly。无需运行主机安装脚本或从源码构建。

请使用 deploy/docker-compose.coolify.yml,不要使用仓库根目录的开发 Compose 文件。它会完全在容器中运行 API、控制台、管理面板、消费者、工作节点、跟踪、实时服务、表单、PostgreSQL、Redis 和 NATS。

此方式不使用 install.sh、源码编译、Docker socket 挂载或主机端口映射。Coolify 负责反向代理、证书和部署。数据保存在 Docker 命名卷中,因此容器可以替换,数据则必须保留。

开始前的准备

  • 一台运行正常、已启用代理的 Coolify 服务器。建议从至少 4 GB 内存开始,并为镜像、邮箱数据和备份留足磁盘。实际需求随使用量增长。
  • 一台 amd64 或 arm64 VPS,能够出站访问 GHCR、Docker Hub、邮件服务商及其 SMTP/IMAP 端口。Warmbly 不是 SMTP 服务器,它通过你连接的邮箱发送。
  • 六个指向 VPS 的 DNS 名称。下文示例使用 app.example.com、admin.example.com、api.example.com、realtime.example.com、track.example.com 和 forms.example.com。请将所有示例替换为自己的域名。
  • 公网端口 80 和 443 可供 Coolify 代理使用。不要将 PostgreSQL、Redis、NATS 或单个应用端口开放到互联网。

实际外联时,建议为跟踪使用独立注册域名,不使用承载控制台和平台邮件的域名的子域。这样可以将营销活动链接的信誉与你自己的网站分开。下文共用示例域名仅用于说明路由;参见邮件送达能力。

通用 GitHub 部署为什么可能失败

Warmbly 是多服务应用,不是单个 Node 或 Go Web 服务器。自动 Nixpacks/Railpack 检测或选择单个 Dockerfile,不会部署它需要的依赖。根目录 Compose 文件还包含源码构建定义、开发默认值和主机端口。专用 Coolify 文件避开这些路径,并要求启动前提供真实秘密值。没有失败部署的日志,不能据此诊断某次具体失败。

1. 创建 Compose 资源

从以下方法中选择一种,两者运行相同镜像。

粘贴文件内容,无需 Git 集成

  1. 在 Coolify 的项目和环境中,选择 + New(新增)。
  2. 选择 Docker Compose Empty(空 Docker Compose)。
  3. 将完整的 Coolify Compose 原始文件粘贴到 Compose 编辑器并保存。

如果只想运行 Warmbly、不打算维护分支,这是最简单的方式。请保留完整文件,包括顶部 x- 锚点和底部 volumes 部分。

使用 GitHub 仓库

  1. 选择 + New(新增),选择公开 Git 仓库,输入 https://github.com/warmbly/warmbly 并检查仓库。
  2. 选择 Docker Compose 构建包;部分版本在构建策略下称为 Compose。不要选择 Nixpacks、Railpack 或 Dockerfile。
  3. 选择分支 main,将 Base Directory(基础目录) 设为 /,Docker Compose Location(Compose 文件位置) 设为 /deploy/docker-compose.coolify.yml。
  4. 保存并加载或重新加载 Compose 定义。确认镜像以 ghcr.io/warmbly/warmbly/ 开头,且没有 build: 条目。

路径相对于仓库根目录。不要指向 /docker-compose.yml,也不要添加自定义构建或启动命令。Git 检出决定使用哪份 Compose 定义,WARMBLY_TAG 决定实际应用版本。如果只希望主动请求时升级,请禁用自动 Git 部署。

2. 设置环境变量

部署前,在此资源的 Environment Variables(环境变量) 中填写以下值。URL 面向浏览器,不是 Docker 服务地址。

APP_URL=https://app.example.com
ADMIN_URL=https://admin.example.com
API_PUBLIC_URL=https://api.example.com
REALTIME_HOST=realtime.example.com
TRACKING_DOMAIN=track.example.com
FORMS_DOMAIN=forms.example.com
WARMBLY_TAG=prod

三个 URL 不要带末尾斜杠。三个主机名值不要包含协议、端口或路径。尤其不要把 :8080 加入 API_PUBLIC_URL,或把 :4000 加入 REALTIME_HOST。文件会自动构建安全 WebSocket URL。

prod 拉取生产发布渠道。需要可重复部署时,将 WARMBLY_TAG 设为 GitHub Releases 中已发布的版本,例如 v0.6.55。所有 Warmbly 服务使用同一标签。不要使用尚未发布完成的标签、latest,或在这个 NATS 服务栈中使用 -kafka 变体。

在可信计算机上生成七个互相独立的秘密值。以下命令只生成随机字符串,不会安装 Warmbly 或修改 VPS:

Coolify 变量生成值的命令
POSTGRES_PASSWORDopenssl rand -hex 32
AUTH_SECRETopenssl rand -hex 32
INTERNAL_API_TOKENopenssl rand -hex 32
NODE_BROKER_TOKENopenssl rand -hex 32
SECRET_KEY_BASEopenssl rand -hex 64
KMS_LOCAL_MASTER_KEYopenssl rand -base64 32
CREDENTIALS_ENCRYPTION_KEYopenssl rand -hex 32

每行分别运行一次命令,只将输出粘贴到对应变量。Coolify 支持时,将它们标为秘密值。部署和运行时必须让 Compose 能读取这些值;这里没有构建阶段。不要粘贴命令本身,也不要让必填值为空。PostgreSQL 密码使用生成的十六进制值,便可直接安全地放入数据库 URL,无需 URL 编码。

同一组已保存变量在服务间复用。AUTH_SECRET 同时作为实时服务的 JWT_SECRET;NODE_BROKER_TOKEN 将仅工作节点使用的内部调用,与面向公网边缘的服务分开保护。无需为每个容器重复填写。

保持加密密钥和数据库凭据稳定

请安全备份这些变量。丢失或替换 KMS_LOCAL_MASTER_KEY 或 CREDENTIALS_ENCRYPTION_KEY,会导致现有加密数据不可读。重新部署时不要重新生成。修改 Coolify 中的 POSTGRES_PASSWORD,不会改变已初始化 PostgreSQL 卷内的密码;应先在 PostgreSQL 中有计划地轮换,再更新客户端。

平台邮件

初始默认值为 MAIL_TRANSPORT=log,因此实例无需邮件中继也能启动。平台邮件只写入服务日志,不实际投递。邀请其他用户或依赖密码重置之前,请添加:

MAIL_TRANSPORT=smtp
EMAIL_ADDRESS=[email protected]
EMAIL_NAME=Warmbly
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=your-smtp-user
SMTP_PASSWORD=your-smtp-password

使用服务商的真实信息和已验证发件人。隐式 TLS 使用 SMTP_PORT=465 和 SMTP_SECURITY=tls。这些变量会传给后端和消费者,包括通知和摘要功能;它们与营销活动邮箱凭据相互独立。参见平台邮件。

3. 将域名分配给对应服务

在 Coolify 中为每个公开服务配置域名。端口后缀选择的是容器内部端口,不是用户在浏览器中填写的端口:

服务Coolify 域名值公开浏览器地址
webhttps://app.example.com:80https://app.example.com
adminhttps://admin.example.com:80https://admin.example.com
backendhttps://api.example.com:8080https://api.example.com
realtimehttps://realtime.example.com:4000wss://realtime.example.com/socket/websocket
trackinghttps://track.example.com:3000https://track.example.com
formshttps://forms.example.com:8090https://forms.example.com

Git 资源中,打开 Domains(域名),选择接收服务并添加域名。Docker Compose Empty 资源则在各服务设置中配置。不同 Coolify 版本的标签和页面名称可能不同,但关键是上表中的服务与端口映射。

不要为 postgres、redis、nats、consumer 或 worker 添加域名或公开端口映射。保持 Coolify 正常的代理管理开启;此文件不提供手动代理标签。也无需与无关资源共用预定义网络。Coolify 会处理这些域名的路由和 HTTPS 证书。

前端容器监听 80 端口,不是 5173/5174。每个 /health 请求都应发送到其所属服务;将 API 域名路由到 web 会得到 HTML,而不是 API 响应。Compose 为各 HTTP 服务定义了就绪检查。消费者和工作节点是后台进程,没有 HTTP 端点。

保留真实客户端 IP 地址

将 TRUSTED_PROXIES、TRACKING_TRUSTED_PROXIES 和 FORMS_TRUSTED_PROXIES 设置为 Coolify 反向代理的实际来源 IP/CIDR,多个值用逗号分隔,然后重新部署。请根据服务器 Docker 网络配置确定,没有通用的 Coolify 子网。绝不要使用 0.0.0.0/0 或 ::/0。只有可信代理可以提供原访问者的转发地址。

安全默认值是不信任任何转发地址。如果位于代理后却未设置,速率限制、审计记录和跟踪分类会将所有访问者视为共用代理 IP。参见网络与代理。

4. 部署并认领实例

  1. 选择 Deploy(部署) 并检查部署日志。Coolify 应拉取镜像,不应编译 Go、Rust、Elixir 或 Node。PostgreSQL、Redis 和 NATS 先启动,再由后端自动执行数据库迁移,随后启动其他服务。

  2. 等待 HTTP 服务健康,且 consumer 和 worker 持续运行。首次迁移可能比普通重启耗时更长。

  3. 通过 Coolify 打开 backend 容器内部终端,执行:

    warmblyctl setup-link
  4. 打开命令打印的一次性 https://app.example.com/setup?token=... 链接,创建所有者账户。应像密码一样保护链接。它在 24 小时后过期;实例尚未认领时,可重新运行命令替换丢失或过期链接。

  5. 在后端终端运行 warmblyctl status。应逐项检查发现的问题,不要把 HTTP 健康检查理解为完整的邮件投递检查。详情见首次运行和实例健康状态。

认领会创建初始平台管理员;不要向真实实例填充演示用户。认领后,注册默认仅限邀请。访问管理面板前,在设置 > 安全中登记通行密钥或 TOTP,再使用该第二因素重新登录。参见管理权限。

确认 https://api.example.com/health 正常响应,控制台打开时没有 CORS 错误,并且管理端节点集群视图出现工作节点。然后连接邮箱,验证受控发送和同步,再将实例用于真实营销活动。部署健康本身不能证明 VPS 能访问邮件服务商。

连接邮箱

SMTP/IMAP 邮箱连接不需要 Google 或 Microsoft 应用注册。此服务栈会验证它们的 TLS 证书,不要为绕过服务商设置而关闭验证。

对于 OAuth 邮箱连接,文件也会将 BOX_GOOGLE_CLIENT_ID、BOX_GOOGLE_CLIENT_SECRET、BOX_GOOGLE_OAUTH_CONNECT、BOX_OUTLOOK_CLIENT_ID 和 BOX_OUTLOOK_CLIENT_SECRET 传给相应服务。Gmail 界面开关遵循 BOX_GOOGLE_OAUTH_CONNECT。请创建自己的服务商应用,按配置中的说明设置回调 URL,再重新部署。这些是邮箱连接客户端,不是“使用 Google 登录”凭据。

更新、备份与现有数据

  • 通过 Coolify 更新,不要使用 Warmbly 应用内更新器。此服务栈设置 UPDATER_URL=none,也从不挂载 Docker socket。先备份,再设置新的已发布 WARMBLY_TAG,或刷新 prod 镜像,并在当前 Coolify 版本中启用重新拉取镜像后部署。仅重启现有容器不会拉取新发布。八个应用镜像应保持同一版本。
  • 保留资源及其卷。 持久卷为 postgres_data、redis_data、nats_data、blobs 和 worker_state。Coolify/Compose 可能给实际 Docker 名称加上资源标识符前缀。普通重新部署会复用它们;删除卷、以其他标识符重建资源,或运行 docker compose down -v,则不会保留这种复用。
  • 数据和秘密值一起备份。 创建 PostgreSQL 逻辑备份,保留 Blob 卷和加密密钥,并将 Redis、NATS 和工作节点状态纳入恢复计划。直接复制运行中的 PostgreSQL 数据目录,不是一致数据库备份。如需协调的整实例快照,先停止应用服务。参见数据控制。
  • 不要假设已有自动备份。 此处 PostgreSQL 是 Compose 服务,不一定是配置了定时备份的 Coolify 托管数据库。必须明确配置并测试备份和恢复流程。
  • 已有安装应按迁移处理,不是全新部署。 此资源会创建自己的卷,不会自动发现安装器部署的数据。保留现有实例和密钥完整,按备份/迁移流程或工作区导出/导入操作。只复制数据库或重新生成加密密钥都不够。数据库迁移在启动时运行,因此恢复旧镜像不保证数据库能回滚。

故障排查

现象检查项
Coolify 构建源码,或因找不到包/启动命令失败选择 Docker Compose 和专用文件路径,或将文件粘贴到 Docker Compose Empty。移除 Nixpacks/Railpack/Dockerfile 设置。
启动前提示必填变量错误填写资源环境中的每个 ${VARIABLE:?} 值,保存并重新部署。粘贴时保留全部 YAML 锚点。
镜像不存在或仓库拒绝访问检查精确发布标签和镜像名。这些是公开 GHCR 镜像,无需 Warmbly Cloud 登录。检查 VPS DNS、网络访问和仓库限制。
端口已分配此文件不发布主机端口。移除从根目录 Compose 复制来的映射;只有 Coolify 代理应绑定公网 80/443。
代理 502 或“没有可用服务器”检查失败服务的运行日志和健康状态,再检查域名对应的内部端口。容器健康检查使用 127.0.0.1。证书错误属于独立的 DNS/代理问题。
重新部署后 PostgreSQL 身份验证失败为已有卷恢复原 POSTGRES_PASSWORD,或在 PostgreSQL 内轮换密码。不要删除数据卷。
控制台能加载,但请求失败API_PUBLIC_URL 必须是外部 HTTPS API 来源,不能有内部端口或 /v1 后缀。API 必须路由到 backend:8080,不是 nginx。修改环境变量后重新部署,让前端 config.js 重新生成。
实时连接断开,或浏览器报告 CSP/来源错误检查 REALTIME_HOST 的 DNS/HTTPS、到 realtime:4000 的路由,以及精确的 APP_URL/ADMIN_URL 来源。文件共享 JWT 密钥,并配置 WebSocket 来源检查和前端 CSP。
收不到重置密码或邀请邮件MAIL_TRANSPORT=log 不会投递邮件。配置 SMTP,检查后端和消费者日志,并确认允许服务商出站端口。
邮箱已连接,但不发送也不同步检查工作节点/消费者日志、心跳、服务商凭据、TLS 和出站 SMTP/IMAP 访问。不要开放入站邮件端口或关闭 TLS 检查。

对于本页未覆盖的失败,请提供 Coolify 版本、VPS 架构、所选发布标签、失败服务名,以及相关部署/运行错误行。务必删去秘密值、认领链接、邮箱凭据和连接字符串。

本指南遵循 Coolify 官方的 Docker Compose、域名路由和健康检查文档。

本页目录