版本: n8n v2.18.0 (master @ 2026-04) 仓库: github.com/n8n-io/n8n.… 定位: Fair-code 许可的工作流自动化平台 (Workflow Automation),对标 Zapier / Make,强调可自托管、代码可扩展、AI 原生。
本文档分为两大部分:
- 第一部分:对 n8n 源码进行体系化、模块级、直至关键类与方法的深度剖析。
- 第二部分:从零搭建生产环境(单机 → Queue 集群 → 多主节点 HA),并给出最佳实践。
目录
- 一、全局认知:定位、License 与技术栈
- 二、Monorepo 结构解剖
- 三、核心模型:Workflow / Node / Connection / Execution
- 四、执行引擎 (packages/core) 深剖
- 五、CLI / Server 进程启动链 (packages/cli)
- 六、HTTP 层:AbstractServer、控制器与 DI
- 七、触发器、Webhook 与 ActiveWorkflowManager
- 八、Queue 模式:Bull + Redis 横向伸缩
- 九、Multi-Main HA:Leader 选举与 PubSub
- 十、节点 (Nodes) 扩展体系
- 十一、Task Runner:代码节点与进程隔离
- 十二、AI / LangChain 节点体系
- 十三、前端 editor-ui (Vue 3 + Pinia)
- 十四、数据模型、加密与凭据
- 十五、生产部署:Docker / Compose / Kubernetes
- 十六、可观测性、备份、运维 SOP
- 十七、安全加固清单
- 十八、从一个实际工作流看整条执行链路
- 附录 A:核心环境变量速查
- 附录 B:常见坑与排障
一、全局认知:定位、License 与技术栈
1.1 产品定位
n8n = "nodemation"(node + automation)。一个可自托管的可视化自动化引擎:
- 400+ 集成节点(HTTP/SaaS/DB/AI/文件/消息队列…)
- 支持 Code 节点(JS / Python)、AI Agent(LangChain)、Sub-workflow、Webhook/Cron/MQ 触发器
- 提供 REST API、Public API、Webhook URL,可以把工作流作为一等 HTTP 服务暴露
1.2 License:Fair-code
n8n 采用 Sustainable Use License(基于 Apache 2.0 + Commons Clause 类约束):
- 源码可读、可自托管、可二次修改
- 商业化托管 n8n 本身(提供 n8n-as-a-Service)受限,需企业许可
- 企业版功能(SSO/SAML/LDAP/高级 RBAC/Log Streaming/Multi-Main)在
*.ee.ts文件中,运行时需要 License Key 激活。代码依然开源(Source Available)。
1.3 技术栈速览
| 层 | 选型 |
|---|---|
| 语言 | TypeScript 全栈 |
| 后端 | Node.js ≥ 22.16,Express 4,TypeORM (SQLite/Postgres/MariaDB/MySQL) |
| 前端 | Vue 3 + Vite + Pinia + Element Plus + VueFlow(画布) |
| 队列 | Bull 4 + Redis(Queue 模式) |
| DI | @n8n/di(自研轻量 IoC,类装饰器) |
| 打包 | pnpm workspaces + Turbo + Biome + ESLint + lefthook |
| 测试 | Jest (unit)、Vitest (FE)、Playwright (E2E)、nock (HTTP mock) |
| Python 代码节点 | @n8n/task-runner-python(沙箱子进程) |
| JS 代码节点 | isolated-vm V8 隔离 |
二、Monorepo 结构解剖
根 pnpm-workspace.yaml 将 40+ 个包聚合。关键包(按依赖层级自底向上):
┌────────────────────────────────────────────────────────┐
│ packages/workflow —— 纯类型 + 纯函数图遍历 │
│ packages/core —— 执行引擎 (WorkflowExecute)│
│ packages/@n8n/config —— 集中式配置 (class-validator) │
│ packages/@n8n/db —— TypeORM entities / repos │
│ packages/@n8n/di —— IoC 容器 │
│ packages/@n8n/decorators —— @Service/@Command/@OnShutdown│
│ packages/cli —— Express Server、命令、Scaling│
│ packages/nodes-base —— 306+ 内置节点(见 ls 统计)│
│ packages/@n8n/nodes-langchain —— AI / LangChain 节点 │
│ packages/@n8n/task-runner —— JS/Python 代码沙箱 runner │
│ packages/frontend/editor-ui —— Vue3 编辑器 │
│ packages/frontend/@n8n/design-system —— UI 组件库 │
│ packages/@n8n/api-types —— FE/BE 共享 DTO │
└────────────────────────────────────────────────────────┘
分层原则:
workflow不依赖core;core不依赖cli;cli粘合所有。- 节点包只依赖
workflow的 interfaces,保证节点可独立发布 (community node)。 editor-ui与后端通过@n8n/api-types共享 DTO,避免类型漂移。
三、核心模型
3.1 Workflow(工作流)
来自 packages/workflow/src/workflow.ts:
class Workflow {
id: string;
nodes: INode[];
connections: IConnections; // 以 source node 为 key
nodeTypes: INodeTypes;
settings: IWorkflowSettings;
staticData: IDataObject; // 节点持久化数据(如 OAuth token)
pinData?: IPinData;
// 图遍历:
getParentNodes(name): string[];
getChildNodes(name): string[];
getStartNode(destination?): INode;
}
重要概念:workflow.connections 的 key 是 source 节点。反向查询父节点必须先用 mapConnectionsByDestination() 反转(见 AGENTS.md 明确规定)。这是因为触发顺序本质是 输出 → 输入,source-indexed 便于 BFS 前向传播。
3.2 Node(节点)
节点 = 一个带 description 的类,描述参数 schema + 一个 execute()(或 trigger / poll / webhook)。
interface INodeType {
description: INodeTypeDescription; // JSON schema-like
execute?(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
trigger?(this: ITriggerFunctions): Promise<ITriggerResponse>;
poll?(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
webhook?(this: IWebhookFunctions): Promise<IWebhookResponseData>;
}
返回的 INodeExecutionData[][] 第一层是 output index(多 branch,如 IF 节点),第二层是 items(一个分支下多条记录)。
3.3 Connection(连接)
连接区分类型(NodeConnectionTypes):
main— 常规数据流ai_tool、ai_languageModel、ai_memory、ai_embedding…(AI 专用,LangChain 节点把 LLM/Memory/Tool 作为"供给连接")
3.4 Execution(执行)
一次运行 Workflow 的实例,持久化到 execution_entity 表。状态机:
new → running → success | error | canceled | crashed | waiting
IRunExecutionData 承载运行时全部状态,重要字段:
executionData.nodeExecutionStack: 待执行栈(核心调度结构)executionData.waitingExecution/waitingExecutionSource: 多输入节点的等待区resultData.runData: 已完成节点 → ITaskDatastartData.runNodeFilter: 局部执行过滤器resumeToken: wait 节点挂起后恢复凭证
四、执行引擎 (packages/core) 深剖
引擎入口:packages/core/src/execution-engine/workflow-execute.ts。
4.1 类 WorkflowExecute
class WorkflowExecute {
private status: ExecutionStatus = 'new';
private readonly abortController = new AbortController();
constructor(
private readonly additionalData: IWorkflowExecuteAdditionalData,
private readonly mode: WorkflowExecuteMode,
private runExecutionData: IRunExecutionData = createRunExecutionData(),
private readonly storedAt: ExecutionStorageLocation = 'db',
) {}
run(opts: RunWorkflowOptions): PCancelable<IRun> { … }
runPartialWorkflow2(…): PCancelable<IRun> { … } // 支持从某节点部分重跑
processRunExecutionData(workflow): PCancelable<IRun> { … } // 核心调度循环
}
注意源码注释 明确禁止把 run() 标 async:因为 PCancelable 在 async 包装后会退化为普通 Promise,失去取消能力。
4.2 核心调度算法(伪代码抽取)
调度本质是一个以 nodeExecutionStack 为主、waitingExecution 为辅的 有向无环遍历 + 多输入栅栏:
loop:
if stack empty:
if waiting empty: break // 完成
else: 处理超时 / 条件恢复
taskData = stack.pop()
node = taskData.node
if node 被 runNodeFilter 排除: continue
if 有未到齐的 main 输入 && !所有源已完成:
放入 waitingExecution 等区, continue
建立 ExecuteContext(IExecuteFunctions) —— 见 4.3
try:
result = await node.execute.call(ctx) # PCancelable
catch (NodeOperationError | NodeApiError):
按 continueOnFail / errorOutput 路由
按 connections 把 result 的每个 branch 推到后继节点的 stack
关键点:
- 调度顺序受
workflow.settings.executionOrder(v0/v1)影响。新版v1优先深度优先,改善并行分支的视觉一致性。 handleCycles()识别环;环内节点需手动用If+Wait终止。- 取消:
abortController.signal与PCancelable配合,activeExecutions.stop()触发TimeoutExecutionCancelledError/ManualExecutionCancelledError。
4.3 执行上下文(Node Execution Contexts)
位于 packages/core/src/execution-engine/node-execution-context/:
| Context | 用于 | 暴露能力 |
|---|---|---|
ExecuteContext | 常规 execute() | helpers.httpRequest / returnJsonArray / getInputData / getCredentials |
ExecuteSingleContext | executeSingle 项级 | 每条 item 独立上下文 |
TriggerContext | trigger() | emit() 推入调度;workflow 激活期长驻 |
PollContext | poll() | 只暴露轮询相关 helper |
WebhookContext | webhook() | req/res + getBodyData/getHeaderData |
HookContext | lifecycle hooks | 无 IO |
LoadOptionsContext | 动态下拉枚举 | 读凭据/调 API 填充 Select |
SupplyDataContext | AI 节点"供给"侧 | 为 Agent 提供 Tool/Memory/LLM |
CredentialsTestContext | 凭据测试按钮 | 单独发一次 API 验 token |
每种 Context 都派生自 BaseExecuteContext,共享:DI-scoped Logger、binaryDataService、expressionResolver(n8n 表达式 {{ $json.a }} 解析)。
4.4 RoutingNode(声明式 HTTP 节点)
packages/core/src/execution-engine/routing-node.ts 提供"配置即节点"——节点只写 routing: { request: { … } },引擎自动组装 HTTP 请求、分页、重试、预处理、后处理。nodes-base 里 70% 的 SaaS 节点都用此模式,见 Airtable、Notion、Slack 节点。
4.5 TriggersAndPollers
- 启动
trigger()返回的长连接(如 IMAP IDLE、MQ 订阅) - 通过
cron调度poll() - 统一把 emit 的数据塞进
WorkflowRunner.run()
五、CLI / Server 进程启动链 (packages/cli)
二进制入口:packages/cli/bin/n8n。命令通过 @Command 装饰器注册,位于 packages/cli/src/commands/:
| 命令 | 作用 |
|---|---|
n8n start | 主进程:HTTP Server + 激活 workflow + (regular 模式下)执行器 |
n8n worker | Queue 模式下从 Redis 拉任务执行 |
n8n webhook | 独立 webhook 进程,只处理 HTTP,把执行推入队列 |
n8n execute / execute-batch | CLI 直跑工作流(运维/CI 场景) |
n8n export / import | 工作流/凭据备份与恢复 |
n8n user-management:reset | 管理员密码重置 |
n8n update:workflow | 批量运维 |
n8n ldap:reset / mfa:disable | 企业版辅助 |
5.1 Start 命令关键路径
packages/cli/src/commands/start.ts:62:
@Command({ name: 'start', … })
export class Start extends BaseCommand {
protected server = Container.get(Server);
override needsCommunityPackages = true;
override needsTaskRunner = true;
async init() { await super.init(); /* DB/License/Nodes/EventBus/Scaling */ }
async run() {
// 1. 启动 HTTP Server
await this.server.start();
// 2. 激活所有 active: true 的 workflow(webhook 注册 + trigger 启动)
await this.activeWorkflowManager.init();
// 3. 启动定期 pruning(老执行记录清理)
Container.get(ExecutionsPruningService).init();
// 4. 启动 Task Runner 子进程
if (this.needsTaskRunner) await Container.get(TaskRunnerModule).start();
// 5. 如果 --open 自动开浏览器
if (flags.open) this.openBrowser();
}
async stopProcess() { … /* 见 start.ts:91 */ }
}
BaseCommand(见 base-command.ts)统一做:
- Crash Journal(断电恢复):把
executing的 execution 标crashed并触发 recovery - DB 初始化(migrations / entities)
- LoadNodesAndCredentials(扫描
packages/nodes-base/dist/**+ 社区 node_modules) - License 校验(
@n8n/licenseSDK 调用) - EventBus 初始化(消息总线)
@OnShutdown优先级化关闭
5.2 启停优先级
用 @OnShutdown(priority) 装饰器(@n8n/decorators),HIGHEST_SHUTDOWN_PRIORITY 先跑(如 push 通知关闭),DB 最后。这保证:
- 不再接受新 HTTP 请求
- 正在跑的 execution 有
N8N_GRACEFUL_SHUTDOWN_TIMEOUT内 wrap-up 机会 - Queue 模式下 worker 不再
getNextJob,但完成手头 job
六、HTTP 层:AbstractServer、控制器与 DI
6.1 AbstractServer
packages/cli/src/abstract-server.ts 抽取三种进程共享的 Express 栈:
- middlewares:
helmet、cookie-parser、rawBody、rateLimit、bodyParser、compression、cors(可关) - webhooks mounting:
/webhook/*、/webhook-test/*、/webhook-waiting/* - health:
/healthz、/healthz/readiness markAsReady()在 listen 后设标志,反向代理/LB 探针才通过
6.2 Server(主进程)
server.ts:72 继承 AbstractServer,额外:
- 静态资源:托管
editor-ui打包产物(EDITOR_UI_DIST_DIR) - 挂载 40+ 控制器(顶部一堆
import '@/controllers/*'—— 副作用式注册,依赖 decorator 收集元数据) - Chat Server(WebSocket for AI Assistant)
- Public API(OpenAPI,
packages/cli/src/public-api/) - Push(SSE 向前端推实时执行状态)
6.3 控制器模式
用自研 DI + 装饰器做类 Nest 写法:
@RestController('/workflows')
export class WorkflowsController {
constructor(private readonly workflowService: WorkflowService) {}
@Get('/')
@GlobalScope('workflow:list')
async list(req: WorkflowRequest.GetMany) { … }
@Post('/')
@Licensed('feat:advancedExecutionFilters')
async create(req: WorkflowRequest.Create) { … }
}
装饰器做了:路由注册、权限 scope 校验(@GlobalScope / @ProjectScope)、License gating、Zod body validation、错误转 HTTP status。这比 Nest 更轻,没有 Module,但也因此所有 Service 都是全局单例。
6.4 DI 容器(@n8n/di)
最小可用 IoC,大致:
// di.ts 心智模型
const registry = new Map<Token, any>();
export function Service() {
return (target) => { registry.set(target, /* lazy singleton */); };
}
export const Container = {
get<T>(token: Token<T>): T { /* resolve constructor deps via reflect-metadata */ }
};
所有 controller/service/repository 都 @Service(),构造器注入。测试里用 mock<T>() + Container.set(Token, mock) 替换。packages/@n8n/backend-test-utils 提供统一 fixture。
七、触发器、Webhook 与 ActiveWorkflowManager
7.1 ActiveWorkflowManager
packages/cli/src/active-workflow-manager.ts:79 是**把"static workflow 定义"→"运行时订阅者"**的桥梁。职责:
- 启动时:
init()拉出active=true的工作流 → 对每一个 trigger node 调trigger(),注册长连接;对每一个 webhook 在webhook_entity写静态路由。 - 运行时:workflow 被保存/删除/切换激活 → 通过
Publisher发布workflow-activated/workflow-deactivatedpub/sub 消息,其他 main / webhook / worker 进程订阅并同步状态。 - 错误重试:激活失败(如外部 API 拒绝)走指数退避
WORKFLOW_REACTIVATE_INITIAL_TIMEOUT → WORKFLOW_REACTIVATE_MAX_TIMEOUT。 - 优雅下线:
@OnShutdown关掉所有 trigger/poller。
7.2 Webhook 路径
从 HTTP 请求到执行一次工作流:
[Client] POST /webhook/abc-xyz
│
▼
AbstractServer.webhookRouter
│
▼
WebhookService.findByPath → 查 webhook_entity 拿到 workflowId、nodeName、method
│
▼
WebhookController.executeWebhook
│ 建立 WebhookContext → node.webhook() → 返回 responseData
▼
WorkflowRunner.run({ workflowData, startNodes:[webhookNode], pinData, triggerToStartFrom })
│
├─ regular 模式: new WorkflowExecute(...).run() 直接本地跑
└─ queue 模式: ScalingService.addJob(...) 丢到 Bull,HTTP 可选择"同步等完成"或立即返回
responseMode 支持三种:
onReceived:webhook 立即 200,背后异步执行lastNode:阻塞到最后一个节点的返回值作为 HTTP bodyresponseNode:由用户显式的Respond to Webhook节点返回(可自定义 status/headers/stream)
八、Queue 模式:Bull + Redis 横向伸缩
8.1 架构
┌──────────────┐ Bull Queue (Redis)
│ Main(s) │ ───────────────────────────► ┌──────────┐
│ Webhook(s) │ job: { executionId, load… } │ Workers │
└──────────────┘ │ (N 个) │
▲ └──────────┘
│ pub/sub (worker-status, job-finished) │
└───────────────────────────────────────────── │
▼
[ DB write result ]
8.2 ScalingService
packages/cli/src/scaling/scaling.service.ts:33:
setupQueue():创建 Bull Queuen8n-jobs,配置maxStalledCount: 0(禁用 Bull 的默认 retry,n8n 自己管)setupWorker(concurrency):queue.process(JOB_TYPE_NAME, concurrency, async job => jobProcessor.processJob(job))setupQueueRecovery():Leader 定期扫描 stuck 的runningexecution(worker 宕机但 Bull 未标 stalled 的边界情况),搬到 crashed 并触发 recovery
8.3 JobProcessor
job-processor.ts:57:worker 里实际执行逻辑。
- 从 DB 加载 execution data
- 组装
WorkflowExecute实例,注册getLifecycleHooksForScalingWorker(这类 hook 不写 DB,只通过 pub/sub 向 main 汇报进度,因为 main 负责最终持久化) - 运行,产出
IRun,通过JobFinishedMessage回传到 main
8.4 为什么要分这么多进程
- main — UI、API、激活 workflow、聚合执行结果
- webhook(可选独立)— 只处理 HTTP 入口,吞吐高,可独立 HPA
- worker — CPU 密集的 workflow 执行,数量随队列深度扩
env 开关:EXECUTIONS_MODE=queue,Redis 连接 QUEUE_BULL_REDIS_HOST/PORT/PASSWORD/DB/TLS。
九、Multi-Main HA:Leader 选举与 PubSub
9.1 问题
多 main 同时跑时,如果都去激活同一个 cron 触发器,会重复触发;且 ActiveWorkflowManager 的内存状态需要同步。
9.2 MultiMainSetup
packages/cli/src/scaling/multi-main-setup.ee.ts(企业版):
- 基于 Redis
SET NX EX实现 租约式 Leader 选举(keyn8n:main-leader,TTL 15s,续约 5s) @OnLeaderTakeover/@OnLeaderStepdown装饰器回调服务,如:- Only Leader: 运行
ExecutionsPruningService、QueueRecovery - All Mains: 响应 API、处理 webhook
- Only Leader: 运行
- 切 Leader 时,
ActiveWorkflowManager响应pubsub.event暂停/接管 trigger
9.3 PubSub 通道(Redis Pub/Sub)
packages/cli/src/scaling/pubsub/:
| Channel | 用途 |
|---|---|
n8n.commands | 跨进程指令:workflow-activated、restart-task-runner、community-package-install |
n8n.worker-response | worker → main 状态同步 |
Publisher / Subscriber 用 ioredis,PubSubRegistry 汇总所有订阅者。业务代码用 @OnPubSubEvent('workflow-activated') 装饰方法。
十、节点 (Nodes) 扩展体系
306 个内置节点 + LangChain 节点包 + 社区节点。一个最小节点:
// packages/nodes-base/nodes/HelloWorld/HelloWorld.node.ts
export class HelloWorld implements INodeType {
description: INodeTypeDescription = {
displayName: 'Hello World',
name: 'helloWorld',
group: ['transform'],
version: 1,
description: 'Greets input items',
defaults: { name: 'Hello World' },
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
properties: [
{ displayName: 'Greeting', name: 'greeting', type: 'string', default: 'Hello' },
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const out = items.map((item, i) => ({
json: { ...item.json, msg: `${this.getNodeParameter('greeting', i)} from n8n` },
}));
return [out];
}
}
10.1 加载
LoadNodesAndCredentials(packages/cli/src/load-nodes-and-credentials.ts)在启动时扫描:
packages/nodes-base/dist/**/*.node.js- 社区包目录(
~/.n8n/nodes/node_modules/n8n-nodes-*) - 企业 gateway 推送的动态包
每个节点按
typeVersion索引(旧工作流可稳定运行旧版本),热更新支持(dev)。
10.2 Community Node 安全
社区节点默认不允许(N8N_COMMUNITY_PACKAGES_ENABLED=true 打开)。新增安全验证 @n8n/scan-community-package,在安装前静态扫描:eval/Function、child_process、可疑网络、权限声明。
十一、Task Runner:代码节点与进程隔离
问题:用户在 Code 节点里写 require('fs').unlinkSync('/') 怎么办?
方案演进:
- 早期:
vm2/isolated-vm在 main 进程隔离 → 仍共享内存 - 现在:独立 Task Runner 子进程,通过 IPC(
node:worker_threads+ 消息协议)接任务
packages/@n8n/task-runner/ 实现 JS runner;packages/@n8n/task-runner-python/ 对应 Python(调 Pyodide/Node-Python bridge)。
Main 进程通过 packages/cli/src/task-runners/ 里 TaskRunnerModule:
- 启动 runner(spawn 或外部 runner via WebSocket)
TaskBroker维护 runner 池,分发 task,超时/内存限制、rate-limit- 支持 Internal(同机子进程)与 External(独立容器,K8s 侧车)两种部署
env:N8N_RUNNERS_ENABLED=true(新安装默认 true),N8N_RUNNERS_AUTH_TOKEN=...,N8N_RUNNERS_MODE=internal|external。
十二、AI / LangChain 节点体系
packages/@n8n/nodes-langchain/ 用 LangChain.js 封装了 Agent、Chain、Memory、Tool、Retriever、Vector Store 等概念为节点:
- AI Agent / AI Chain — 根 Executor 节点,通过
ai_languageModel、ai_memory、ai_tool输入端口连接 - Chat Trigger —
/chatWebSocket / SSE 长连接前端 chat UI - MCP Server — 把一个 workflow 暴露为 Model Context Protocol server,LLM 可以外部调用
- Vector Stores:Pinecone、Qdrant、Supabase、PGVector、In-Memory
- Tool = 子 workflow(通过
SupplyDataContext暴露给 Agent),这实现了 可视化 Agent 编排
关键源码:
- Agent 执行器:
nodes/agents/Agent/Agent.node.ts - LangChain ↔ n8n 数据桥:
utils/N8nLangChainLogger.ts - MCP:
mcp/core/McpServer.ts,在ScalingService.setupQueue中注入 session store(Redis-backed,支持分布式)
环境变量:N8N_AI_ENABLED、N8N_AI_ASSISTANT_ENABLED(编辑器右侧 AI 助手)。
十三、前端 editor-ui (Vue 3 + Pinia)
13.1 技术细节
- Vite 构建,Pinia store(
packages/frontend/editor-ui/src/stores/) - 画布基于 VueFlow,自定义节点卡片、连线、minimap
- 表达式编辑器:CodeMirror 6 + 自研
@n8n/codemirror-lang({{ $json.a }}语法高亮 + 自动补全) - 设计系统:
packages/frontend/@n8n/design-system/,所有颜色/间距/字号走 CSS 变量(见 AGENTS.md CSS Variables 章节) - i18n 强制化:所有 UI 文案必须走
@n8n/i18n包
13.2 实时通信
前端通过 /rest/push(SSE 或 WebSocket,取决于 N8N_PUSH_BACKEND)订阅:
executionStarted/nodeExecuteBefore/nodeExecuteAfter/executionFinishedtestWebhookReceived(test-webhook 进入后自动选中节点)
后端 Push 服务(packages/cli/src/push/)在执行 lifecycle hook 里发事件,按 sessionId 路由到具体浏览器 tab。
十四、数据模型、加密与凭据
14.1 主要表(TypeORM packages/@n8n/db/)
workflow_entity:name / nodes(JSON)/ connections(JSON)/ active / settings / staticDataexecution_entity+execution_data(二进制分表,执行详情大 JSON 单独存,便于 pruning)credentials_entity:name / type / data(AES-256-CBC 加密) / shared via ACLwebhook_entity:path / method / workflowId / nodeId(激活时写入,查找 O(1))user、role、project、shared_workflow、shared_credentials:RBACsettings(kv)、installed_packages(社区节点版本)、workflow_history(变更记录)
14.2 加密密钥
~/.n8n/config 存 encryptionKey(首次启动生成,32 字节随机)。生产必须显式设 N8N_ENCRYPTION_KEY,否则:
- 不同 pod/节点解密不一致 → 凭据失效
- 备份恢复后密钥丢失 → 所有凭据报废
加密入口:packages/core/src/encryption/。
14.3 凭据热替换(Credential Overwrites)
企业场景:集中 Vault 提供真 secret,编辑器里保留占位符。CredentialsOverwrites 从 CREDENTIALS_OVERWRITE_DATA env 或 HTTP endpoint(CREDENTIALS_OVERWRITE_ENDPOINT)动态取值注入。
十五、生产部署:Docker / Compose / Kubernetes
15.1 最小单机
docker volume create n8n_data
docker run -d --name n8n --restart unless-stopped \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
-e N8N_HOST=n8n.example.com \
-e N8N_PROTOCOL=https \
-e WEBHOOK_URL=https://n8n.example.com/ \
-e N8N_ENCRYPTION_KEY=$(openssl rand -hex 32) \
-e GENERIC_TIMEZONE=Asia/Shanghai \
-e TZ=Asia/Shanghai \
-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_HOST=pg \
-e DB_POSTGRESDB_DATABASE=n8n \
-e DB_POSTGRESDB_USER=n8n \
-e DB_POSTGRESDB_PASSWORD=*** \
docker.n8n.io/n8nio/n8n
把默认 SQLite 换 Postgres:SQLite 在并发执行 > 5 会写冲突。
15.2 Queue 模式 Docker Compose
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes: [pg:/var/lib/postgresql/data]
healthcheck:
test: [CMD-SHELL, pg_isready -U n8n]
interval: 5s
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
volumes: [redis:/data]
# ========= 公共环境 =========
x-n8n-common: &n8n-common
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
environment: &n8n-env
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.example.com/
GENERIC_TIMEZONE: Asia/Shanghai
TZ: Asia/Shanghai
N8N_RUNNERS_ENABLED: "true"
N8N_LOG_LEVEL: info
N8N_METRICS: "true"
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: "336" # 14 天
N8N_PAYLOAD_SIZE_MAX: "32" # MB
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_started }
n8n-main:
<<: *n8n-common
command: ["n8n", "start"]
ports: ["5678:5678"]
n8n-webhook:
<<: *n8n-common
command: ["n8n", "webhook"]
ports: ["5679:5678"] # 反代只把 /webhook/* 打到这
n8n-worker:
<<: *n8n-common
command: ["n8n", "worker", "--concurrency=10"]
deploy:
replicas: 3
volumes: { pg: {}, redis: {} }
反代(Nginx / Traefik)按路径分流:
/webhook/*、/webhook-test/*、/webhook-waiting/*→n8n-webhook- 其他(
/、/rest/*、/api/*) →n8n-main
15.3 Kubernetes 骨架(含 HPA)
关键 Chart 要素:
- Deployment: n8n-main(replicas ≥ 2 需企业版 Multi-Main;社区版 replicas=1)
- Deployment: n8n-webhook
command: [n8n, webhook],HPA 基于 CPU/QPS - Deployment: n8n-worker
command: [n8n, worker, --concurrency=10],HPA 基于 Bull 队列长度(用prometheus-adapter读n8n_queue_waiting_count) - Postgres(RDS / CloudNativePG)+ Redis(ElastiCache / bitnami chart)
- Secrets:
N8N_ENCRYPTION_KEY、DB、Redis;绝不放 ConfigMap - Ingress:
nginx.ingress.kubernetes.io/proxy-body-size: 64m;webhook host 可独立 N8N_PROXY_HOPS=1(Ingress 加一跳)让express.set('trust proxy', …)正确取 IP
15.4 Graceful Shutdown
K8s preStop 设 sleep 10,terminationGracePeriodSeconds: 60,env N8N_GRACEFUL_SHUTDOWN_TIMEOUT=45。优先级 hook 会确保:正在跑的 execution 写完 DB、Leader lease 释放。
十六、可观测性、备份、运维 SOP
16.1 Metrics (Prometheus)
N8N_METRICS=true → /metrics 端点。关键指标:
n8n_active_workflow_countn8n_queue_waiting_count/active_count/completed_count/failed_countn8n_execution_duration_seconds(labeled byworkflow_id,status)- 默认 Node.js 进程指标(GC、event loop lag)
建议的告警:
- Bull 队列
waiting_count> 阈值持续 5min → 扩 worker execution failed rate> 2% → 业务方排查- Main Leader lease 丢失且未选出新 Leader > 30s → Redis 故障
16.2 日志
N8N_LOG_LEVEL=info(生产);调试debugN8N_LOG_OUTPUT=console,file+N8N_LOG_FILE_LOCATION=/var/log/n8n/- 企业版
Log Streaming可把Event Bus事件推到 Syslog / Kafka / Webhook
16.3 备份
必须备份三样:
- Postgres 全库 ——
pg_dump,每日增量 + WAL 归档 ~/.n8n/config—— 含encryptionKey(未用 env 覆盖时)- binary data 存储(
N8N_DEFAULT_BINARY_DATA_MODE=filesystem:/home/node/.n8n/binaryData;或 S3:bucket 版本 + 生命周期)
恢复测试每季度演练一次。务必保留 N8N_ENCRYPTION_KEY 的同一值,否则凭据全部失效。
16.4 Pruning(自动清理)
EXECUTIONS_DATA_PRUNE=true 打开,EXECUTIONS_DATA_MAX_AGE=336(小时),EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000。服务:ExecutionsPruningService,Leader 独占跑。
十七、安全加固清单
-
N8N_ENCRYPTION_KEY显式设置,32 bytes 随机;使用 KMS/Secrets Manager -
N8N_BASIC_AUTH_ACTIVE已废弃,改 User Management(默认开,首账号即管理员);若暴露到公网必须启用 MFA -
N8N_SECURE_COOKIE=true,仅 HTTPS -
N8N_BLOCK_ENV_ACCESS_IN_NODE=true防 Code 节点读取宿主 env -
N8N_PAYLOAD_SIZE_MAX=16限制 webhook body(默认 16 MB) -
N8N_RUNNERS_MODE=external生产更安全;N8N_RUNNERS_AUTH_TOKEN强随机 - 禁用社区节点(
N8N_COMMUNITY_PACKAGES_ENABLED=false)或审计安装列表 - 关闭公开注册(
N8N_DISABLE_USER_MANAGEMENT=false但不暴露 invite 路由到公网) - Postgres 用只有 n8n 库权限的账号;Redis 设密码 +
requirepass+ 网络隔离 - Webhook 路径带 UUID(n8n 默认);对外 API 网关加 IP 白名单 + WAF
- Code 节点默认关
eval;N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES=true - 启用企业版
Audit Log或自行从 EventBus 拉user.*/workflow.*事件 -
/healthz/readiness在 Ingress 配 basic-auth 或deny,防泄露版本
十八、从一个实际工作流看整条执行链路
场景:用户调用 POST /webhook/orders → 校验 → 写 Postgres → 发 Slack → 返回 JSON。
Webhook ─main─▶ Function (validate) ─main─▶ Postgres (insert) ─main─▶ Slack ─main─▶ Respond to Webhook
整条链(Queue 模式):
1. Nginx 把请求转到 n8n-webhook pod
2. AbstractServer 路由 → WebhookController.executeWebhook
3. WebhookService.findByPath 查 webhook_entity → 命中 workflowId=42
4. 组装 WebhookContext → 调 Webhook.node.ts 的 webhook() → 取 body
5. WorkflowRunner.run():
- 创建 execution_entity(status=new)
- ScalingService.addJob({ executionId, loadStaticData:false })
6. Bull 把 job 入 Redis
7. n8n-worker pod 的 queue.process 触发 JobProcessor.processJob:
a. 从 DB 加载 executionData
b. new WorkflowExecute(additionalData, 'webhook', data, 'db')
c. .run({ startNode: Webhook-node }) → PCancelable
d. processRunExecutionData 调度循环:
- pop Webhook 节点 → 已有输出数据 → 前向传递
- push Function 节点 → 调 Task Runner 子进程执行 user code
- push Postgres 节点 → RoutingNode 构建 SQL → pg client 写库
- push Slack 节点 → RoutingNode 发 Incoming Webhook
- push Respond to Webhook → 通过 pub/sub 把 response 发回 n8n-webhook pod
e. hooks 把每个 nodeExecuteAfter 通过 pub/sub 推 main/webhook,供前端 SSE
8. n8n-webhook pod 收到 RespondToWebhookMessage → HTTP res.json(body)
9. JobProcessor 完成 → JobFinishedMessage → main 持久化 execution result、发 push
这条链路解释了:为什么一个 webhook 在 Queue 模式下的 "response latency" = Redis RTT × 3 + 节点执行和,以及为什么 Respond to Webhook 节点能跨进程返回——通过 pub/sub 而非共享内存。
附录 A:核心环境变量速查
| 变量 | 默认 | 作用 | |||
|---|---|---|---|---|---|
N8N_HOST | localhost | 对外访问主机名(编辑器链接用) | |||
N8N_PORT | 5678 | 监听端口 | |||
N8N_PROTOCOL | http | https 用于生成正确 webhook URL | |||
WEBHOOK_URL | = N8N_PROTOCOL://N8N_HOST:PORT/ | webhook 注册到外部平台的基 URL | |||
N8N_ENCRYPTION_KEY | auto | 生产必设 | |||
DB_TYPE | sqlite | postgresdb / mysqldb / mariadb | |||
EXECUTIONS_MODE | regular | queue 启用集群模式 | |||
QUEUE_BULL_REDIS_HOST | — | Redis 主机 | |||
QUEUE_BULL_REDIS_PASSWORD | — | Redis 密码 | |||
N8N_METRICS | false | /metrics Prometheus | |||
N8N_LOG_LEVEL | info | `error | warn | info | debug` |
N8N_PAYLOAD_SIZE_MAX | 16 | webhook body 最大 MB | |||
EXECUTIONS_DATA_PRUNE | false | 开启自动清理 | |||
EXECUTIONS_DATA_MAX_AGE | 336 | 小时 | |||
N8N_RUNNERS_ENABLED | true | Task Runner | |||
N8N_RUNNERS_AUTH_TOKEN | — | 内部通信 token | |||
N8N_GRACEFUL_SHUTDOWN_TIMEOUT | 30 | 秒 | |||
N8N_PROXY_HOPS | 0 | 反代跳数 | |||
N8N_COMMUNITY_PACKAGES_ENABLED | true | 社区节点开关 | |||
N8N_AI_ENABLED | true | AI 节点可见性 | |||
GENERIC_TIMEZONE | America/New_York | Cron 时区 | |||
EXECUTIONS_TIMEOUT | -1 | 全局执行超时秒 | |||
N8N_DISABLE_PRODUCTION_MAIN_PROCESS_WEBHOOKS | false | 让 main 不再处理生产 webhook(独立 webhook 进程用) |
附录 B:常见坑与排障
-
"Workflow with ID X is currently not active" 通常是 multi-instance 共享 DB 但 ActiveWorkflowManager 没有正确同步 → 检查 pub/sub(Redis 是否被清 key、网络分区)。
-
Webhook 在 queue 模式下超时
Respond to Webhook必须在所有节点完成前执行(用responseMode: responseNode),否则 LB 会先断开。检查 Nginxproxy_read_timeout。 -
凭据突然全部失效 99% 是
N8N_ENCRYPTION_KEY变了。检查:Helm chart 重建 Secret?env 没注入?恢复方案:拿旧 key 进容器、导出凭据 JSON 明文、用新 key 重新导入。 -
SQLite 并发崩溃(
database is locked) 老部署生产仍用 SQLite。迁移到 Postgres:n8n export:workflow --all --output=/tmp/wf.json→ 新库n8n import:workflow --input=/tmp/wf.json(凭据同理)。 -
Worker OOM 大 binary data(PDF、图片)被塞进
INodeExecutionData.binary。切N8N_DEFAULT_BINARY_DATA_MODE=filesystem或s3,数据走外存,INodeExecutionData里只留引用。 -
Cron 触发器多次触发 Multi-main 无 Leader 选举 → 每个 main 都在跑。要么降成单 main,要么买企业版开 Multi-Main。
-
前端执行日志不更新 Push 通道被反代缓冲。Nginx 里对
/rest/push加proxy_buffering off; proxy_read_timeout 3600s;。 -
Community Node 安装后不生效 Queue 模式下必须把包同时装进 main、webhook、worker 三种镜像;推荐自建镜像
RUN npm i -g n8n-nodes-xxx统一分发。
结语:n8n 是一个把"工作流引擎 + 节点生态 + 前端编辑器 + 执行集群 + AI Agent"缝合得极其工整的工程体。读懂它的三条主线——Workflow 图模型、执行上下文 (ExecuteContext)、Queue 模式进程分工——就基本掌握了 80% 的源码。剩下 20% 是 306 个节点的集成细节与企业版的 SSO/审计/多租户,按需深入即可。
部署方面的金律只有三条:Postgres 起步、Queue 模式横扩、K8s Helm 管 Secret。严格执行本文第十五、十六、十七节清单,即可得到生产可用的高可用 n8n 集群。