用 Docker Compose 把 Novu 社区版部署到 VPS:一个 HOST_NAME 让 Dashboard 公网可用
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
如果你曾在本地把 Novu 跑起来,再把整个 docker compose 目录原封不动搬上 VPS,大概率会撞见这样的场景:容器全部在跑,docker compose ps一片绿色,可浏览器打开http://<vps-ip>:4000却永远转圈。问题不在镜像也不在端口,而在.env里一行不起眼的配置:HOST_NAME的默认值是http://localhost,等于把所有对外地址钉死在本机回环上,容器一旦搬到公网机器,外部浏览器自然连不进来。
这篇内容讲如何把社区版 Novu 部署到一台公网 VPS,并把HOST_NAME配置正确。跟着做下去,最后的效果是团队直接通过http://<vps-ip>:4000打开 Dashboard。
这次部署总共会拉起哪些容器
先看 docker/community/docker-compose.yml,整个部署由 6 个容器组成:
api、worker、ws、dashboard四个业务服务,镜像 tag 在 compose 文件里写死为ghcr.io/novuhq/novu/*:3.19.0;mongodb(数据全部落在这里,volume 持久化);redis(队列与缓存,业务服务都depends_on它 healthy 之后才启动)。
资源方面,官方自托管概览(docs/community/self-hosting-novu/overview.mdx)给出的单 VM 建议规格:
| 组件 | 建议规格(单 VM) |
|---|---|
| Novu 四个服务(API / Worker / WS / Dashboard) | 合计 4 vCPU、8 GB RAM |
| MongoDB | 2 GB RAM、20 GB 磁盘 |
| Redis | 2 GB RAM,开启 AOF |
| 对象存储 | 10 GB 起步 |
文档自己说明这是通用建议,可以按负载调整。机器上需要 Docker 加 docker compose(v2 插件即可),以及curl和openssl,后者多数系统预装。
用最短路径把服务拉起来
一条命令:
curl -fsSL https://raw.githubusercontent.com/novuhq/novu/next/docker/community/setup.sh | bash想把部署文件放进指定目录,通过NOVU_DIR指定(例如~/novu):
curl -fsSL https://raw.githubusercontent.com/novuhq/novu/next/docker/community/setup.sh | NOVU_DIR=~/novu bashsetup.sh 会代劳的事,按顺序拆开看:
- 把
docker-compose.yml和.env.example下载到目标目录(默认./novu); - 用
openssl rand为JWT_SECRET、STORE_ENCRYPTION_KEY、NOVU_SECRET_KEY生成加密随机值; - 基于
.env.example创建.env并写入这些密钥,同时把文件权限收紧到600; - 执行
docker compose up -d启动全部服务。
一个容易忽略的细节:如果目标目录里已经有.env,脚本只检查三个密钥是否为空,缺哪个补哪个,不会动你已有的配置。所以升级部署时重跑脚本是安全的。
为什么改一个 HOST_NAME,所有对外地址就换了
打开 docker/community/.env.example,HOST_NAME那一行是:
HOST_NAME=http://localhost紧跟其后的一组变量全部由它推导:
VITE_API_HOSTNAME=${HOST_NAME}:3000 VITE_WEBSOCKET_HOSTNAME=${HOST_NAME}:3002 API_ROOT_URL=${HOST_NAME}:3000 S3_LOCAL_STACK=${HOST_NAME}:4566推导链是这样的:Dashboard 容器在 compose 文件里只注入了VITE_API_HOSTNAME和VITE_WEBSOCKET_HOSTNAME两个变量——前端拿前者去调 REST API、拿后者连 WebSocket 推送;API 服务自己用API_ROOT_URL拼回调与重定向地址。根值一变,整条链路的前端请求地址、实时连接地址、API 自引用地址同时跟着换,不需要再逐服务修改。
VPS 场景下,把这一行改成服务器公网 IP(有域名就填域名):
# 将 <vps-ip-address> 替换为你的 VPS 公网 IP HOST_NAME=http://<vps-ip-address>按官方部署文档(docs/community/self-hosting-novu/deploy-with-docker.mdx)的取值约定:本机部署用http://localhost,VPS 部署用服务器 IP 或域名。
上线前核对三个必填密钥
| 变量 | 用途 | 要求 |
|---|---|---|
JWT_SECRET | API 签发、校验 JWT 令牌 | 加密随机值 |
STORE_ENCRYPTION_KEY | 加解密第三方 provider 凭据 | 必须恰好 32 个字符 |
NOVU_SECRET_KEY | Novu 全局密钥 | 加密随机值 |
走 setup.sh 安装的话,这三个值已经随机生成好;如果是手动从仓库取文件部署的,.env.example里它们是空的,注释里也给了生成命令(openssl rand -hex 32,STORE_ENCRYPTION_KEY用openssl rand -hex 16得到 32 字符),上线前必须自行填好。密钥一旦投入使用就不能再换:换了JWT_SECRET所有已签发的令牌失效,换了STORE_ENCRYPTION_KEY存量凭据直接解不开。
启动、放行端口与排查手段
改完.env后(无论改了HOST_NAME还是别的变量),启动或重启都是同一条命令:
docker compose up -dsetup 脚本跑完会在终端输出类似这样的结果:
Novu is running: Dashboard : http://localhost:4000 API : http://localhost:3000 WebSocket : http://localhost:3002注意输出里写的是localhost——那只是脚本的固定文案,VPS 上要把三处都换成你的 VPS IP 才是真实入口。
公网放行只需要下面这张清单,防火墙/安全组照此配置:
| 服务 | 端口 | 说明 |
|---|---|---|
| Dashboard | 4000 | 团队入口,浏览器访问 |
| API | 3000 | REST API,SDK 与 SDK 触发都打这里 |
| WebSocket | 3002 | 实时推送通道 |
| Worker | 3004 | 仅健康检查用,无需对外放行 |
启动是否完成可以用两个信号交叉确认:
docker compose ps:每个服务的状态转成healthy。API/WS/Worker 的探针打/v1/health-check,Dashboard 用根路径 HTTP 200 判断;docker compose logs -f看日志,重点盯 api 和 mongodb 是否报错。
如果浏览器始终打不开http://<vps-ip>:4000,第一嫌疑永远是防火墙或云厂商安全组没放 4000(以及后面要用的 3000、3002)——官方把"用防火墙限制只开放必要端口"列进了 VPS 安全事项,放行时别顺手把全部端口开了。
公网暴露前的三件事
1. 关掉新用户注册。默认配置下,任何能打开 Dashboard 的人都能注册账号,公网部署不允许这样。在.env里加:
DISABLE_USER_REGISTRATION=true该变量作用在 API 服务上,默认是false。置为true后,注册请求会被 API 拒绝,返回400 Bad Request,消息为Account creation is disabled——实现就一行:注册用例里判断该变量为true时直接抛 BadRequestException;已有用户的登录不受影响。改完照常docker compose up -d重启生效。
2. 套一层反向代理并启用 HTTPS。按官方 VPS 安全清单:防火墙只放必要端口、配置 SSL/TLS 证书走 HTTPS、定期更新 Docker 镜像和主机系统、.env里保持强且唯一的密钥、考虑用 Nginx 之类的反向代理加一层隔离。如果反向代理之后要以子路径对外(比如company.com/novu),需要设置GLOBAL_CONTEXT_PATH作为全局基础路径,再配各服务的API_CONTEXT_PATH、WS_CONTEXT_PATH等变量;文档特别提醒这些变量要设置在 Novu 的所有服务上,因为服务之间耦合较紧,漏配一个就会断链。
3. 确认密钥文件权限。.env里存着全部密钥,setup.sh 建文件时会chmod 600,手动创建的记得同样处理。
社区版的边界,以及跑起来之后怎么接入
为了把 Docker 部署做得足够简单,官方做了两个明确的取舍(部署文档原文立场):
- MongoDB 和所有服务跑在同一台机器上,官方强烈建议生产部署前先把数据库解耦出去;
- 对象存储用的是 LocalStack,而不是真正的 S3。
功能边界上,按 docs/community/self-hosting-novu/overview.mdx 与 docs/community/self-hosted-and-novu-cloud.mdx:自托管版不支持社交登录(GitHub、Google 等),账号只能用邮箱加密码登录;SSO、RBAC、MFA 这类能力属于 Novu Cloud,社区版里没有。API 参考两边是同一套,区别只是你的 SDK 指向自己的 API 地址而不是官方云。
服务起来之后,把通知接进自己的产品:
- Inbox 前端组件:
backendUrl指向http://<vps-ip>:3000,socket 地址指向http://<vps-ip>:3002; - Server SDK:
serverURL同样指向http://<vps-ip>:3000,鉴权用的 secret 就是.env里的NOVU_SECRET_KEY。
完成标志:浏览器打开http://<vps-ip>:4000,看到 Novu 的登录页面——从这一刻起,VPS 上的自托管 Novu 对团队公网可用。之后的日常就是:改配置先改.env,再docker compose up -d重启,用docker compose ps确认全部 healthy。
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考