首页
/ Novu Docker 自托管与本地开发指南:从 Compose 依赖栈到生产级部署

Novu Docker 自托管与本地开发指南:从 Compose 依赖栈到生产级部署

2026-09-08 11:38:12作者:尤峻淳Whitney

本指南以仓库 docker/Readme.md 为核心,完整讲解 Novu 项目在 Docker 生态下的两种用法:用 docker/local 的 Compose 文件拉起本地开发所需的 MongoDB、Redis、LocalStack 等依赖服务,然后从源码运行 Novu 应用本身;以及参考 docker/community 下的镜像编排栈做面向生产的自托管部署。读完本文,你将能独立完成本地开发环境搭建、理解各 Compose 文件差异与端口分配、正确配置 JWT / 加密等核心密钥,并掌握 Docker 自托管时的环境变量体系与安全加固要点。

docker/ 目录到底放了什么

仓库根目录的 docker/ 下只有两个子目录,职责划分非常清晰:

  • docker/local/:面向本地开发的 Compose 编排,含 docker-compose.ymldocker-compose.agent.ymldocker-compose.e2e.ymldocker-compose.local.yml 四个文件;
  • docker/community/:面向自托管部署的 docker-compose.yml.env.example 以及自动化安装脚本 setup.sh

原文档特别强调了一句话,也是理解整套编排的钥匙:

The docker/local/ compose file is for local development dependencies only — it does not start the Novu API, Worker, WebSocket, or Dashboard services.

docker/local/docker-compose.yml 只启动开发依赖(数据库、缓存、对象存储模拟器),并不会启动 Novu 自己的任何应用进程。API、Worker、WebSocket、Dashboard 这些服务需要通过 pnpm dev:portless 从源码直接跑起来(详见下文),这是本地开发模式与自托管镜像模式在架构上的根本区别。

docker/local:一份"只含依赖"的 Compose 清单

启动全部依赖服务

从仓库根目录执行:

docker compose -f docker/local/docker-compose.yml up -d

启动后你会得到四个容器。以仓库实际内容为准,docker-compose.yml 中定义的依赖及其端口如下:

服务 镜像 容器名(默认) 端口(默认) 用途
localstack localstack/localstack:0.14.5 ${LOCALSTACK_DOCKER_NAME:-localstack_main} 4566${DOCKER_LOCALSTACK_PORT:-4566} SERVICES=s3 模式提供 S3 对象存储模拟,用于上传工作流附件、邮件图片等
mongo mongo:8.0.17 ${MONGO_DOCKER_NAME:-mongo_main} 27017${DOCKER_MONGO_PORT:-27017} 主数据库,存储组织、工作流、订阅者、消息等业务数据
redis redis ${REDIS_DOCKER_NAME:-redis_main} 6379${DOCKER_REDIS_SERVICE_PORT:-6379} 队列与缓存,支撑 Worker 的作业分发与缓存服务
clickhouse clickhouse/clickhouse-server:24.3-alpine ${CLICKHOUSE_DOCKER_NAME:-clickhouse_main} 8123(HTTP)、9000(Native) 消息活动(activity)等分析型数据存储,端口对应变量 DOCKER_CLICKHOUSE_HTTP_PORTDOCKER_CLICKHOUSE_NATIVE_PORT

值得注意的编排细节:

  • 全部采用 network_mode: bridge,容器之间不共享自定义网络,而是把端口直接暴露给宿主机,方便宿主机上的 Node 进程(即从源码运行的 Novu 服务)通过 localhost 直连。
  • 数据卷默认挂到本机临时目录:Mongo 与 LocalStack 使用 ${TMPDIR:-/tmp/...},意味着默认情况下数据不持久化到命名卷,重启或清理后数据会丢失,只适合短期开发。
  • 镜像与端口都可被环境变量覆盖,Compose 语法形如 '${DOCKER_MONGO_PORT:-27017}:27017',即在未设置该环境变量时回退到默认端口。若你本机 27017 已被占用,可通过设置 DOCKER_MONGO_PORT 等变量避开冲突。
  • 健康检查:mongo 通过 mongo --eval "printjson(rs.status())"、redis 通过 redis-cli ping、localstack 通过 aws s3 ls 探测,均设置 retries: 5interval: 10s,为后续其它服务的 depends_on.condition: service_healthy 预留前提。
  • ClickHouse 声明了资源配额deploy.resources.limits.memory: 2Greservations.memory: 1G,提示该服务是四个依赖中内存占用最大的一个。

只启动需要的服务(规避端口冲突)

如果你的机器上已有 MongoDB 或 Redis 在运行,没必要启动全套:

docker compose -f docker/local/docker-compose.yml up -d redis

同理,mongolocalstackclickhouse 都可以作为单独的参数传入,docker compose ... up -d <service> 只会拉起你指定的服务。

同一目录下的其它三个 Compose 文件

除了默认的 docker-compose.ymldocker/local 下还有三个变体,分别面向不同的开发场景:

  • docker-compose.local.yml:它才是"应用容器化"的入口,用仓库根目录作为 build context、分别基于 apps/api/Dockerfileapps/worker/Dockerfileapps/ws/Dockerfile 构建 apiworkerws 三个服务镜像。也就是说,想用 Docker 镜像而不是 pnpm dev 跑本地源码时,可借助此文件 docker compose -f docker/local/docker-compose.local.yml build
  • docker-compose.agent.yml:面向 Novu Agent 相关开发场景的依赖栈,服务集合与基础版基本一致(localstack、mongo、redis、clickhouse),但没有为 ClickHouse 设置资源配额,Mongo 数据卷也使用 /data/db 挂载点。
  • docker-compose.e2e.yml:e2e 测试专用,只声明了一个 localstack 服务来模拟 S3,配合 apps 下的 e2e 测试套件(如 apps/api/e2eapps/webhook/e2e)运行,Mongo、Redis 等在 e2e 场景下通常由测试环境单独提供。

从源码结构可以推断:基础 docker-compose.yml 中的 Mongo 卷挂载写为 ${TMPDIR:-/tmp/mongo}:/db/data,而 agent 变体使用 /data/db;如果自定义挂载目录时,请以你实际使用的文件为准。

前置条件与从源码启动本地开发

原文档给出了明确的版本要求:

  • Docker 与 Docker Compose v2 插件;
  • Git
  • Node.js v22.23.0 与 pnpm 11.x——仅当你要从源码运行 Novu 应用服务时需要。

pnpm 11 的版本要求并非空穴来风:仓库根 package.jsonsetup:project 脚本明确使用了 npx --yes pnpm@11.0.9 i,因此建议在仓库根执行 pnpm --version 确认大版本为 11。

拉起依赖后再装依赖、跑服务

# 1. 启动依赖服务(MongoDB、Redis、LocalStack;可加 clickhouse)
docker compose -f docker/local/docker-compose.yml up -d

# 2. 从仓库根安装项目依赖并构建
npm run setup:project

# 3. 以 portless 模式启动整套应用
pnpm dev:portless

其中两个脚本在 package.json 中的真实定义为:

  • setup:projectnpx --yes pnpm@11.0.9 i && node scripts/setup-env-files.js && pnpm build——安装全部 workspace 依赖、执行 setup-env-files.js 生成各应用所需的 .env 文件,并完成一次构建;
  • dev:portlessnode scripts/mprocs-dev.mjs——通过 mprocs 并行拉起 API、Worker、WebSocket、Dashboard 等应用服务。

完整的本地开发教程见仓库内 docs/community/run-in-local-machine.mdx。按默认配置,Dashboard 开发服务器运行在 http://127.0.0.1:4201(该地址来自原文档;而生产自托管模式下 Dashboard 默认端口是 4000,二者不要混淆)。

密钥配置:绝不能拿默认值上线

原文档给出了一条铁律:

While we provide example secrets for getting started, you should NEVER deploy your Novu setup using the defaults provided.

必改的三个密钥

修改 apps/ 下各服务的 .env 文件(setup:project 已通过 setup-env-files.js 生成),至少以下变量必须换成你自己的随机值:

变量 作用 要求
JWT_SECRET API 生成与校验 JWT 签名 加密随机字符串,可用 openssl rand -hex 32 生成
STORE_ENCRYPTION_KEY 加密 / 解密集成商(provider)凭据 必须恰好 32 字符,可用 openssl rand -hex 16 生成
NOVU_SECRET_KEY 平台签名密钥(自托管必需的随机密钥) 加密随机字符串,openssl rand -hex 32

第三项 NOVU_SECRET_KEY 虽未在原文档正文出现,但 docker/community/.env.examplesetup.sh 都把它列为"必须随机生成"的密钥,属于与 JWT_SECRET 同级的安全底线,建议一起修改。

setup.sh 的实现看,官方对密钥随机化的处理方式是:

  • openssl rand -hex 32 生成 JWT_SECRET
  • openssl rand -hex 16 生成 STORE_ENCRYPTION_KEY(16 字节十六进制 = 32 个字符,恰好满足"32 characters"要求);
  • openssl rand -hex 32 生成 NOVU_SECRET_KEY
  • 脚本会先检查 .env 中这些键是否为空,为空才写入随机值,随后把 .env 权限收紧为 chmod 600

Redis TLS 配置

如果连接的是启用 TLS 的 Redis 实例,可在 .env 中加入:

REDIS_TLS={"servername":"localhost"}
REDIS_CACHE_SERVICE_TLS={"servername":"localhost"}

前者作用于业务队列使用的 Redis,后者作用于缓存服务(cache service)使用的 Redis。docker/community/docker-compose.yml 中的 REDIS_HOST / REDIS_PORT / REDIS_PASSWORD / REDIS_CACHE_SERVICE_HOST / REDIS_CACHE_SERVICE_PORT 变量即负责把这两套 Redis 连接信息注入各容器。

面向生产自托管的 docker/community 编排栈

原文档在"Configuration"一节坦承,仓库默认编排为简化做了取舍,生产上并不理想:

  • 数据库与应用服务跑在同一台机器上;
  • 存储使用 LocalStack 或本地文件系统后端,而不是真正的 S3。

因此文档强烈建议:正式部署前先把数据库与应用解耦。若希望直接得到接近生产的镜像编排,可以参考 docker/community/docker-compose.yml——它定义的正是完整六件套:redismongodbapiworkerwsdashboard

社区版栈的服务构成与关键差异

docker/local 相比,community 栈有两个显著不同:

  1. 应用不再是源码进程,而是 ghcr.io 发布的镜像(当前仓库锁定为 3.19.0 版本,例如 ghcr.io/novuhq/novu/api:3.19.0worker:3.19.0ws:3.19.0dashboard:3.19.0);
  2. MongoDB 开启认证:通过 MONGO_INITDB_ROOT_USERNAME / MONGO_INITDB_ROOT_PASSWORD 初始化 root 账号,MONGO_URL 也相应带上了 ?authSource=admin,见 .env.example

服务间通过 depends_onservice_healthy 条件控制启动顺序:api 等 mongodbredis 健康,dashboardapiworker 健康。所有容器都启用了 json-file 日志驱动并限制为 max-size: 50mmax-file: 5,且 restart: unless-stopped

默认端口一览

服务 端口 说明
API 3000${API_PORT:-3000} REST 接口,健康检查命中 /v1/health-check
Worker 3004${WORKER_PORT:-3004} 后台任务处理,健康检查命中 /v1/health-check
WebSocket 3002${WS_PORT:-3002} 实时通道,健康检查命中 /v1/health-check
Dashboard 4000(固定) 控制台前端,健康检查探测 HTTP 200
MongoDB 27017 仅容器间可达,未发布到宿主机
Redis 6379 仅容器间可达,未发布到宿主机

需要重点理解的环境变量

.env.example 为准,除三大密钥外还有几组变量直接影响运行方式:

  • 运行模式NODE_ENV 取值可为 devtestproductioncilocalIS_SELF_HOSTED=trueIS_V2_ENABLED=trueIS_API_IDEMPOTENCY_ENABLEDIS_API_RATE_LIMITING_ENABLEDIS_NEW_MESSAGES_API_RESPONSE_ENABLED 等开关控制功能特性;
  • 连接信息MONGO_URLMONGO_MIN_POOL_SIZE(默认 5)、MONGO_MAX_POOL_SIZE(默认 10)、REDIS_HOSTREDIS_PORTREDIS_DB_INDEX=2(在 compose 中固定为 2,避免与应用默认 DB 冲突)、REDIS_CACHE_SERVICE_*
  • 对象存储S3_LOCAL_STACK=http://localhost:4566S3_BUCKET_NAME=novu-localS3_REGION=us-east-1 以及 AWS_ACCESS_KEY_ID=test / AWS_SECRET_ACCESS_KEY=test——即本地仍默认指向 LocalStack,若接入真实 S3 需要整体替换这组变量;
  • 对外地址HOST_NAME=http://localhostAPI_ROOT_URLFRONT_BASE_URL=http://localhost:(4000|4200)VITE_API_HOSTNAMEVITE_WEBSOCKET_HOSTNAME,它们决定 Dashboard 前端调用 API 与 WebSocket 的目标地址;
  • 可观测性SENTRY_DSN 留空;New Relic 默认关闭(NEW_RELIC_ENABLED=false),开启时才需填 NEW_RELIC_APP_NAMENEW_RELIC_LICENSE_KEY
  • 队列分片BROADCAST_QUEUE_CHUNK_SIZE=100MULTICAST_QUEUE_CHUNK_SIZE=100 控制广播 / 组播任务的批大小。

用 setup.sh 一键自托管

不想手工复制 .env 与生成密钥时,可直接执行 setup.sh

# 在当前目录就地使用仓库内的 docker-compose.yml 与 .env.example 启动
./docker/community/setup.sh

# 或在空白目录使用(脚本会下载官方 compose 与 env 模板到 ./novu 并启动)
bash setup.sh

脚本行为可以从源码确认:

  1. 依赖检查:curldockeropenssl 缺一不可,且要求 Docker Compose v2(检测 docker compose version);
  2. 若当前目录已有 docker-compose.yml.env.example 则就地使用,否则下载到 ./novu
  3. 生成 .env 并自动填充 JWT_SECRETSTORE_ENCRYPTION_KEYNOVU_SECRET_KEY 三个随机密钥(对已存在但为空的项做补写),然后 chmod 600 收紧权限;
  4. 执行 docker compose up -d 并提示访问入口:Dashboard http://localhost:4000、API http://localhost:3000、WebSocket http://localhost:3002,日志查看用 docker compose logs -f

更进一步的部署路径

小结:两套 Compose,一种心智模型

docker/Readme.md 与仓库中的实际编排对照后可以得出清晰的结论:docker/local 回答的是"本地开发时数据库与队列从哪来",docker/community 回答的是"生产环境整套系统如何以镜像方式运行"。前者需要配合 Node.js v22.23.0 + pnpm 11.x 与 pnpm dev:portless 从源码启动应用,后者则开箱即用、通过 .env 注入配置。无论哪条路径,JWT_SECRETSTORE_ENCRYPTION_KEY(32 字符)、NOVU_SECRET_KEY 都是必须替换的随机密钥,也是从"能跑"走向"敢上线"的第一步。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389