在 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或arm64VPS,能够出站访问 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 集成
- 在 Coolify 的项目和环境中,选择 + New(新增)。
- 选择 Docker Compose Empty(空 Docker Compose)。
- 将完整的 Coolify Compose 原始文件粘贴到 Compose 编辑器并保存。
如果只想运行 Warmbly、不打算维护分支,这是最简单的方式。请保留完整文件,包括顶部 x- 锚点和底部 volumes 部分。
使用 GitHub 仓库
- 选择 + New(新增),选择公开 Git 仓库,输入
https://github.com/warmbly/warmbly并检查仓库。 - 选择 Docker Compose 构建包;部分版本在构建策略下称为 Compose。不要选择 Nixpacks、Railpack 或 Dockerfile。
- 选择分支
main,将 Base Directory(基础目录) 设为/,Docker Compose Location(Compose 文件位置) 设为/deploy/docker-compose.coolify.yml。 - 保存并加载或重新加载 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_PASSWORD | openssl rand -hex 32 |
AUTH_SECRET | openssl rand -hex 32 |
INTERNAL_API_TOKEN | openssl rand -hex 32 |
NODE_BROKER_TOKEN | openssl rand -hex 32 |
SECRET_KEY_BASE | openssl rand -hex 64 |
KMS_LOCAL_MASTER_KEY | openssl rand -base64 32 |
CREDENTIALS_ENCRYPTION_KEY | openssl 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 域名值 | 公开浏览器地址 |
|---|---|---|
web | https://app.example.com:80 | https://app.example.com |
admin | https://admin.example.com:80 | https://admin.example.com |
backend | https://api.example.com:8080 | https://api.example.com |
realtime | https://realtime.example.com:4000 | wss://realtime.example.com/socket/websocket |
tracking | https://track.example.com:3000 | https://track.example.com |
forms | https://forms.example.com:8090 | https://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. 部署并认领实例
-
选择 Deploy(部署) 并检查部署日志。Coolify 应拉取镜像,不应编译 Go、Rust、Elixir 或 Node。PostgreSQL、Redis 和 NATS 先启动,再由后端自动执行数据库迁移,随后启动其他服务。
-
等待 HTTP 服务健康,且
consumer和worker持续运行。首次迁移可能比普通重启耗时更长。 -
通过 Coolify 打开
backend容器内部终端,执行:warmblyctl setup-link -
打开命令打印的一次性
https://app.example.com/setup?token=...链接,创建所有者账户。应像密码一样保护链接。它在 24 小时后过期;实例尚未认领时,可重新运行命令替换丢失或过期链接。 -
在后端终端运行
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、域名路由和健康检查文档。