Novu 3.19.0 配置参数详解
Novu 3.19.0 自托管环境变量体系详解:参数三层结构与生效链路实测、全量参数表、密钥/地址/Mongo/Redis/S3/功能开关逐类语义与生产配置模板。
Novu 3.19.0 配置参数详解
目标:讲透 Novu 3.19.0 自托管部署的环境变量体系——参数从
.env文件到容器生效的完整链路、全量参数清单、逐类语义与实测行为。全部结论在 Ubuntu 22.04 + Docker Compose 部署的 Novu 3.19.0 上实测(部署过程见 《Docker Compose 部署 Novu 指南》,组件定位见 《Novu 3.19.0 介绍与选型指南》)。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64)
- Novu 3.19.0(官方 Compose 发行形态:api/worker/ws/dashboard + mongo:8.0.17 + redis:alpine)
- 宿主端口:API 8148、WS 8149、Dashboard 8150
- 参数体系:官方
.env.example(2.2KB,5 组共 40+ 键)+docker-compose.yml中各服务environment:透传白名单 - 版本核对命令:
curl -sS http://192.168.146.128:8148/v1/health-check,实测返回apiVersion: 3.19.0
二、前置条件
- 已按部署指南完成 Novu 3.19.0 的 Compose 部署且 6 服务 healthy;
- 能编辑部署目录下的
docker-compose.yml与.env; - 理解 Compose 变量注入规则:
.env是变量源,compose 文件里${VAR}引用后写入服务environment:,变量只在容器创建时注入,改完必须docker compose up -d重建才生效。
三、参数查看与修改方式
3.1 参数的三层结构
Novu 自托管没有配置文件,参数体系分三层(实测结构):
.env文件:所有变量的唯一取值来源(部署目录下,与 compose 文件同目录);- compose 文件的
environment:白名单:每个服务只接收显式列出的变量,.env里写了但没被某服务引用的键对该服务无效; - 运行时容器环境:最终生效值,用
printenv核对。
# 核对 api 服务实际生效的关键变量(实测回显)
cd /opt/novu && docker compose exec api printenv | grep -E '^(NODE_ENV|IS_SELF_HOSTED|DISABLE_USER_REGISTRATION|PORT)='
NODE_ENV=local
PORT=3000
DISABLE_USER_REGISTRATION=true
IS_SELF_HOSTED=true
# worker 服务端口与队列库序号(实测回显)
docker compose exec worker printenv | grep -E '^(PORT|REDIS_DB_INDEX)='
REDIS_DB_INDEX=2
PORT=3004
3.2 修改参数并令其生效(实测全链路)
以官方提供的注册开关 DISABLE_USER_REGISTRATION 为例(它默认不在官方 compose 透传白名单里,需两步):
第 1 步,.env 追加变量:
cd /opt/novu && echo 'DISABLE_USER_REGISTRATION=true' >> .env
第 2 步,在 compose 文件 api 服务的 environment: 段追加透传行(与 IS_SELF_HOSTED: ${IS_SELF_HOSTED} 同级):
IS_SELF_HOSTED: ${IS_SELF_HOSTED}
DISABLE_USER_REGISTRATION: ${DISABLE_USER_REGISTRATION}
第 3 步,重建使配置注入(compose 自动识别 environment 变化的服务并 Recreate,实测输出节选):
docker compose up -d
Container worker Healthy
Container api Healthy
第 4 步,实测验证生效——新邮箱注册被拒:
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' -X POST http://192.168.146.128:8148/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"email":"newuser@quzuo.net","password":"Q1w2e3r4#","firstName":"New","lastName":"User"}'
curl -sS -X POST http://192.168.146.128:8148/v1/auth/register -H 'Content-Type: application/json' \
-d '{"email":"newuser@quzuo.net","password":"Q1w2e3r4#","firstName":"New","lastName":"User"}'
HTTP 400
{"error":"Bad Request","statusCode":400,"timestamp":"2026-10-21T14:35:54.355Z","path":"/v1/auth/register","message":"Account creation is disabled","ctx":{"error":"Bad Request","statusCode":400}}
⚠️ 只改
.env不改 compose 白名单、或改完不执行up -d,参数都不生效。实测中「改了没反应」的问题几乎全部出在这两处。
3.3 布尔/开关类参数的取值习惯
开关统一用字符串布尔(true/false),compose 里引用时建议写成 ${VAR} 并在 .env 落 true/false 字面量;留空字符串与 false 在 Novu 的布尔解析中均按关闭处理,但显式写 false 更可读。
四、参数总览
按功能域分组的全量参数表(取值均为本环境实测值;「生效服务」列来自官方 compose 的 environment 白名单实测归纳):
| 功能域 | 参数 | 实测取值 | 生效服务 | 一句话语义 |
|---|---|---|---|---|
| 密钥 | JWT_SECRET | 64 位 hex | api, ws | JWT 签名密钥(必填) |
| 密钥 | STORE_ENCRYPTION_KEY | 32 字符 hex | api, worker | 渠道凭证加解密(必填,须恰好 32 字符) |
| 密钥 | NOVU_SECRET_KEY | 64 位 hex | api | 环境根密钥/演示密钥来源(必填) |
| 密钥 | SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME | 15d | api, worker | 收件箱订阅者 JWT 有效期 |
| 地址 | HOST_NAME | http://192.168.146.128 | (作为 .env 内插值根) | 对外主机名根 |
| 地址 | VITE_API_HOSTNAME | ${HOST_NAME}:8148 | dashboard | 前端拼 API 地址 |
| 地址 | VITE_WEBSOCKET_HOSTNAME | ${HOST_NAME}:8149 | dashboard | 前端拼 WS 地址 |
| 地址 | API_ROOT_URL | ${HOST_NAME}:8148 | api, worker | API 对外根地址 |
| 地址 | FRONT_BASE_URL | ${HOST_NAME}:8150 | api | dashboard 对外根地址 |
| 地址 | API_CONTEXT_PATH/WS_CONTEXT_PATH/WEBHOOK_CONTEXT_PATH/FRONT_BASE_CONTEXT_PATH/GLOBAL_CONTEXT_PATH | 空 | api, worker, ws, dashboard | 反向代理统一前缀 |
| 运行 | NODE_ENV | local | 全部 | 环境名(dev/test/production/ci/local) |
| 运行 | IS_SELF_HOSTED | true | api | 自托管标识(必须 true) |
| 运行 | IS_V2_ENABLED | true | api | v2 API 体系开关 |
| 运行 | IS_API_IDEMPOTENCY_ENABLED | false | api | 触发幂等开关 |
| 运行 | IS_API_RATE_LIMITING_ENABLED | false | api | API 限流开关 |
| 运行 | IS_NEW_MESSAGES_API_RESPONSE_ENABLED | true | api | messages 接口新版响应结构 |
| 运行 | DISABLE_USER_REGISTRATION | true(实测开启) | api | 禁止新用户注册 |
| 运行 | IS_EMAIL_INLINE_CSS_DISABLED | false | worker | 邮件模板内联 CSS 开关 |
| 运行 | IS_USE_MERGED_DIGEST_ID_ENABLED | false | worker | digest 合并 ID 策略 |
| Mongo | MONGO_URL | mongodb://root:***@mongodb:27017/novu-db?authSource=admin | api, worker, ws | 连接串 |
| Mongo | MONGO_INITDB_ROOT_USERNAME/PASSWORD | root / 演示口令 | mongodb | 初始管理员(仅空卷首启生效) |
| Mongo | MONGO_MAX_POOL_SIZE / MONGO_MIN_POOL_SIZE | 10 / 5 | api, worker, ws | 连接池上下限 |
| Mongo | MONGO_AUTO_CREATE_INDEXES | true | api | 首启自动建索引 |
| Redis | REDIS_HOST / REDIS_PORT | redis / 6379 | api, worker, ws | 队列 Redis 地址 |
| Redis | REDIS_PASSWORD | 空 | api, worker, ws | 队列 Redis 密码 |
| Redis | REDIS_DB_INDEX | 2(compose 写死) | api, worker | BullMQ 队列所在 db |
| Redis | REDIS_CACHE_SERVICE_HOST / PORT | 空 / 6379 | api, worker | 缓存实例(留空=缓存关闭) |
| Redis | REDIS_SKIP_VERSION_CHECK | false | api, worker | 经代理连 Redis 且无 INFO 命令时置 true |
| S3 | S3_LOCAL_STACK | ${HOST_NAME}:4566 | api, worker | S3 兼容端点 |
| S3 | S3_BUCKET_NAME / S3_REGION | novu-local / us-east-1 | api, worker | 桶名/区域 |
| S3 | AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | test / test | api, worker | S3 凭证 |
| 端口 | API_PORT | 3000 | api | 容器内 API 端口 |
| 端口 | WS_PORT | 3002 | ws | 容器内 WS 端口 |
| 端口 | WORKER_PORT | 3004(compose 默认) | worker | 容器内 worker 端口 |
| 队列 | BROADCAST_QUEUE_CHUNK_SIZE | 100 | worker | 广播触发单批处理量 |
| 队列 | MULTICAST_QUEUE_CHUNK_SIZE | 100 | worker | 组播触发单批处理量 |
| 观测 | SENTRY_DSN | 空 | api, worker | Sentry 错误上报 |
| 观测 | NEW_RELIC_ENABLED / APP_NAME / LICENSE_KEY | false / 占位 / 占位 | 全部 | New Relic APM |
⚠️ 官方
.env.example中MONGO_URL=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongodb:27017/novu-db?authSource=admin使用了 .env 内嵌变量插值(Compose v2.24+ 支持)。改 Mongo 口令时只改MONGO_INITDB_ROOT_PASSWORD一处即可,不要把 MONGO_URL 写死,否则两处易失配导致 api 连库失败。
五、核心参数分类详解
5.1 密钥三件套(JWT_SECRET / STORE_ENCRYPTION_KEY / NOVU_SECRET_KEY)
- JWT_SECRET:api 与 ws 共用,签发/校验登录态与收件箱 JWT。更换后所有已登录会话与已签发的组件 token 立即失效,用户需重新登录;
- STORE_ENCRYPTION_KEY:用于加密各渠道 Provider 的接入凭证(如 SMTP 密码、Twilio token)。⚠️ 官方要求必须恰好 32 字符(
openssl rand -hex 16产出),长度不对时集成凭证无法加解密;该密钥更换后旧凭证将解密失败,需重新录入全部集成凭证; - NOVU_SECRET_KEY:环境根密钥。注册组织/环境后实际发起 API 调用时使用的是各环境的 ApiKey(见 API 使用篇),该根密钥是密钥体系的种子,务必妥善保存。
三键生成的实测命令与产物:
openssl rand -hex 32 # JWT_SECRET / NOVU_SECRET_KEY
openssl rand -hex 16 # STORE_ENCRYPTION_KEY(32 字符)
fade86f0d43ee13a31d528da5d1acfa41aeabddee5fa49e32b23ed2e54ad0105
b9c562f194b51df3054236f1a18de9af
5.2 地址与前端拼接(HOST_NAME 与 VITE_* 四件套)
HOST_NAME 是插值根;真正决定浏览器行为的是 dashboard 服务读到的 VITE_API_HOSTNAME/VITE_WEBSOCKET_HOSTNAME 与 api/worker 读到的 API_ROOT_URL、FRONT_BASE_URL。实测要点:
- dashboard 是静态前端,构建时模板把这些值渲染进页面,值必须是浏览器可达地址(从办公室访问服务器就写服务器 IP/域名,不能写 localhost);
- 宿主端口改过映射后(如 API 宿主 8148 → 容器 3000),这四项里的端口必须写宿主端口;
- 改完执行
docker compose up -d重建 dashboard/api/worker,浏览器强刷(Ctrl+Shift+R)后生效。
反向代理场景:GLOBAL_CONTEXT_PATH=novu + API_CONTEXT_PATH=api + WS_CONTEXT_PATH=ws 可让 API/WS 挂在 https://company.com/novu/api、https://company.com/novu/ws 路径下;dashboard 恒以自身端口/域名独立服务,不受这组变量控制。
5.3 MongoDB 组(连接串与初始化)
MONGO_URL:api/worker/ws 三服务共用,连接novu-db库;实测 worker 内printenv确认注入;MONGO_INITDB_ROOT_USERNAME/PASSWORD:只在数据卷为空的首启生效(mongo 镜像初始化逻辑)。首启后改口令需进 mongo 改用户密码并同步 MONGO_URL,否则 api 报认证失败;MONGO_MAX_POOL_SIZE=10/MONGO_MIN_POOL_SIZE=5:Node 驱动连接池,高并发触发场景按 CPU/内存上调(16GB 内存机器可到 20/10);MONGO_AUTO_CREATE_INDEXES=true:首启自动建全部索引,生产保持 true(关闭需手工建索引,无运维收益)。
5.4 Redis 组(队列与缓存分离)
- 队列固定走
REDIS_HOST:REDIS_PORT的 db2(compose 写死REDIS_DB_INDEX: 2,实测容器内生效值 2),承载 BullMQ 任务队列(trigger-handler/standard/process-subscriber/ws_socket_queue 等队列实测可见); REDIS_CACHE_SERVICE_HOST/PORT是可选的第二用途:给 Inbox feed 与未读数等热点查询做缓存。实测留空时缓存关闭;开启时官方建议小规模部署与队列共用同一 Redis 实例(填同值),大规模部署分离独立实例;REDIS_SKIP_VERSION_CHECK=true适用于经不支持 INFO 命令的代理连 Redis 的托管场景,常规直连保持 false。
5.5 S3 组(存储端点)
S3_LOCAL_STACK默认指向${HOST_NAME}:4566,官方 compose 并未内置 S3/localstack 服务——该默认值在纯官方 compose 部署里指向一个不存在的端点,实测不影响 api 健康与 In-App 消息主链路;需要附件/大内容存储时自建 MinIO 等 S3 兼容服务后改此项;AWS_ACCESS_KEY_ID=test/AWS_SECRET_ACCESS_KEY=test是 localstack 习惯默认凭证,对接真实 S3/MinIO 时替换为真实凭证并补S3_BUCKET_NAME(需预先创建桶)。
5.6 功能开关(IS_* 与 DISABLE_*)
IS_SELF_HOSTED=true:自托管必须保持,影响多处云服务逻辑分支;IS_API_RATE_LIMITING_ENABLED:API 层限流开关,默认 false;生产建议 true(限流阈值由服务端内置策略控制);IS_API_IDEMPOTENCY_ENABLED:触发接口幂等(相同 transactionId 去重),对触发源可能重试的业务建议 true;IS_V2_ENABLED=true/IS_NEW_MESSAGES_API_RESPONSE_ENABLED=true:v3.19.0 默认开启的新 API 体系,保持默认即可;DISABLE_USER_REGISTRATION=true:实测生效(第三章 3.2,HTTP 400Account creation is disabled);注意它默认不在 compose 白名单,需按 3.2 两步接入。
5.7 队列批次(BROADCAST_QUEUE_CHUNK_SIZE / MULTICAST_QUEUE_CHUNK_SIZE)
worker 每次从队列取出的广播/组播批次大小(默认各 100)。广播触发(to 为 topic 或大批 subscriber)时批次过大会拉高单 job 内存,批次过小增加调度开销;默认值在 8GB 内存机器上无需调整,万级订阅者广播场景可降至 50 观察内存。
5.8 观测与上报(SENTRY_DSN / NEW_RELIC_*)
SENTRY_DSN留空即不上报;自托管环境保持留空(错误看本地日志docker compose logs);NEW_RELIC_ENABLED=false时其余 NEW_RELIC_* 无需配置;开启需提供 LICENSE_KEY,仅在已采购 New Relic 时使用;- 3.x 的 Trace/StepRun 深度观测依赖 ClickHouse(官方 compose 未含),未部署 ClickHouse 时 worker 日志出现
Failed to create ClickHouse table,属预期噪音,不影响通知主链路(实测触发与消息落地均正常)。
六、生产配置模板
按部署规模给出两档推荐配置(差异项列出,其余保持官方默认):
小规模(≤1 万订阅者/日,8GB 内存单机,官方基线形态):
NODE_ENV=production
MONGO_MAX_POOL_SIZE=10
MONGO_MIN_POOL_SIZE=5
REDIS_CACHE_SERVICE_HOST=redis # 与队列共用实例,开热点缓存
REDIS_CACHE_SERVICE_PORT=6379
IS_API_RATE_LIMITING_ENABLED=true
IS_API_IDEMPOTENCY_ENABLED=true
DISABLE_USER_REGISTRATION=true # 建好管理账号后关闭公开注册
IS_SELF_HOSTED=true
适用场景:单业务线、In-App+Email 为主、无独立 SRE 团队的团队自用平台;MongoDB 与 Redis 与业务服务同机部署。
中规模(1 万-10 万订阅者/日,16GB+ 内存,MongoDB 独立实例 + 缓存独立实例):
NODE_ENV=production
MONGO_MAX_POOL_SIZE=20
MONGO_MIN_POOL_SIZE=10
MONGO_URL=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@<mongo-host>:27017/novu-db?authSource=admin
REDIS_HOST=<queue-redis-host> # 队列 Redis 独立实例,开 AOF
REDIS_CACHE_SERVICE_HOST=<cache-redis-host> # 缓存 Redis 独立实例
IS_API_RATE_LIMITING_ENABLED=true
IS_API_IDEMPOTENCY_ENABLED=true
DISABLE_USER_REGISTRATION=true
BROADCAST_QUEUE_CHUNK_SIZE=50 # 大广播场景下调批次防内存尖峰
适用场景:多业务线共用、含 SMS/Push 多渠道、需要附件存储(另配 MinIO/S3 并改 S3_* 五项)。两档均需按 5.1 完成三密钥自生成,演示口令一律替换。
七、常见问题(FAQ)
Q1:改了 .env 为什么不生效?
三个可能:没有执行 docker compose up -d 重建(变量只在容器创建时注入);该键不在目标服务的 compose environment: 白名单(如 DISABLE_USER_REGISTRATION,需按 3.2 加透传行);改了 MONGO_URL 但 MONGO_INITDB_ROOT_PASSWORD 没同步。用 docker compose exec <服务> printenv | grep <键> 看实际生效值定位。
Q2:STORE_ENCRYPTION_KEY 长度校验报错?
该键必须恰好 32 字符。用 openssl rand -hex 16 生成(32 个 hex 字符),不要用 hex 32(64 字符)。
Q3:把 REDIS_CACHE_SERVICE_HOST 填了值但 Inbox 查询没变快?
缓存默认关闭,填写后需重建 api/worker 生效;且缓存只覆盖 Inbox feed/未读数与触发链路 DAL 热点查询,低流量场景感知不明显。
Q4:Mongo 口令改动后 api 起不来?
首启后 Mongo 用户已固化在数据卷里。正确顺序:进 mongo 改用户密码 → 同步 .env 两处(MONGO_INITDB_ROOT_PASSWORD 与 MONGO_URL 若写死)→ docker compose up -d。若首启时就失配导致初始化失败,down -v 清卷重建(⚠️ 清卷丢数据)。
Q5:worker 日志大量 Failed to create ClickHouse table?
3.x 观测特性依赖 ClickHouse,官方 compose 未包含,报错不影响通知主链路,可忽略;需要 trace 时自建 ClickHouse 并补相关变量。