Self-hosted n8n 部署指南:Docker Compose、Postgres、VPS 与 NAS 生产基线
这篇文章记录了我在贵阳实验室的实战过程。我坚信,在技术下行的时代,程序员唯一的护城河就是通过 AI 建立属于自己的数字资产。
先给结论
- ✓ 个人低频试用可以从 SQLite 起步;长期运行、多人使用或准备扩展 Queue Mode 时,直接使用 Postgres 更稳。
- ✓ 生产部署必须固定 n8n 镜像版本与 N8N_ENCRYPTION_KEY,数据库和 Redis 不应直接暴露到公网。
- ✓ 反向代理后同时核对 WEBHOOK_URL 与 N8N_EDITOR_BASE_URL,避免 Webhook、OAuth 回调和编辑器链接使用错误域名。
- ✓ 备份不是只复制 Docker volume:至少要保存数据库、n8n 数据目录、加密密钥和部署配置,并实际演练恢复。
- ✓ 单实例出现持续排队、CPU 饱和或执行隔离需求后,再升级 Queue Mode;主实例与 Worker 必须共享数据库、Redis 和同一加密密钥。
适合谁读
- ● 正在把 n8n / workflow-automation / self-hosted / docker 落到真实项目里的开发者。
- ● 不想只看概念,希望知道取舍、边界、风险和下一步怎么做的独立开发者。
- ● 正在做技术选型、工具链治理、自动化工作流或个人数字资产建设的读者。
2026-07 更新说明:本文已从“能跑起来的 Docker 示例”重构为生产基线。删除了容易过期的套餐价格与运行额度,取消
latest镜像和数据库公网端口示例,并补充版本固定、Editor URL、私有网络、恢复演练与 Queue Mode 升级边界。
先给结论:Self-hosted n8n 的最小生产组合
公网或内网入口
↓
Caddy / Nginx / Cloudflare Tunnel
↓
n8n 主实例
↓
Postgres
这套最小组合适合个人开发者、小团队和中低并发 AI Workflow。它不包含 Redis 和 Worker,目的是先把域名、凭据、数据库和备份做对。
出现持续排队、执行任务抢占主实例资源或需要横向扩容时,再升级为:
反向代理
↓
n8n Main ── Redis ── n8n Worker × N
└──────── Postgres ────────┘
不要一开始就把 Queue Mode、多个 Worker、对象存储和复杂监控全部塞进同一个 Compose。部署复杂度应由真实负载触发,而不是由教程长度触发。
VPS、NAS、SQLite、Postgres 怎么选
| 场景 | 推荐数据库 | 公网入口 | 是否需要 Queue Mode |
|---|---|---|---|
| 本地试用、少量手动工作流 | SQLite | 不需要 | 不需要 |
| 个人长期运行、定时任务、Webhook | Postgres | VPS 反代或受控 Tunnel | 暂不需要 |
| NAS 内网部署、无公网 IP | Postgres | Cloudflare Tunnel / 受控反代 | 暂不需要 |
| 多人使用、执行量稳定增长 | Postgres | 正式域名 + HTTPS | 观察队列和资源再决定 |
| 多 Worker、横向扩容 | Postgres | 正式域名 + HTTPS | 需要 Redis + Queue Mode |
n8n 官方文档说明,自托管默认使用 SQLite,也支持 Postgres;Queue Mode 官方建议使用 Postgres,并明确不推荐与 SQLite 组合。数据库选择不是“SQLite 一定会坏”,而是要看持久运行、扩展和恢复要求。
部署前先准备四样东西
- 一个固定域名,例如
n8n.example.com。 - 一个不会随容器重建而变化的
N8N_ENCRYPTION_KEY。 - 独立保存的数据库密码,不写进 Git 仓库。
- 明确的镜像版本或 digest,不直接使用
latest。
推荐目录:
n8n-stack/
├── compose.yml
├── .env # 仅保存在服务器,不提交
├── .env.example # 只保留变量名
└── backups/
.gitignore 至少包含:
.env
backups/
*.sql
Docker Compose 生产基线
下面的配置故意不映射 Postgres 的 5432 端口。n8n 与数据库通过 Docker 私有网络通信;外部只需要访问反向代理后的 n8n。
services:
postgres:
image: ${POSTGRES_IMAGE:?pin POSTGRES_IMAGE}
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-n8n}
POSTGRES_USER: ${POSTGRES_USER:-n8n}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}"]
interval: 10s
timeout: 5s
retries: 10
networks:
- backend
n8n:
image: ${N8N_IMAGE:?pin N8N_IMAGE}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}
DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
N8N_HOST: ${N8N_HOST:?set N8N_HOST}
N8N_PROTOCOL: https
N8N_PORT: 5678
N8N_EDITOR_BASE_URL: https://${N8N_HOST}/
WEBHOOK_URL: https://${N8N_HOST}/
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY}
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: ${EXECUTIONS_DATA_MAX_AGE:-168}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-Asia/Shanghai}
TZ: ${GENERIC_TIMEZONE:-Asia/Shanghai}
volumes:
- n8n_data:/home/node/.n8n
expose:
- "5678"
networks:
- frontend
- backend
networks:
frontend:
backend:
internal: true
volumes:
postgres_data:
n8n_data:
为什么要固定镜像
把 .env 中的 N8N_IMAGE 和 POSTGRES_IMAGE 固定到经过验证的版本或 digest。升级时先修改测试环境,确认数据库迁移和关键工作流正常,再更新生产环境。
错误:N8N_IMAGE=docker.n8n.io/n8nio/n8n:latest
正确思路:N8N_IMAGE=经过测试并锁定的版本或 digest
本文不把某个当前版本硬编码成“永远推荐版本”,因为版本号会变化。部署当天应对照 n8n Release Notes 和官方升级说明确认目标版本。
三个决定部署成败的环境变量
N8N_ENCRYPTION_KEY
n8n 用它加密数据库中的凭据。官方文档说明,系统首次启动会自动生成随机密钥,也允许使用自定义密钥。生产环境必须把自定义值保存在密码管理器或受控 Secret 中。
必须满足:
- 重建容器后保持不变。
- 迁移服务器时一起迁移。
- Queue Mode 下主实例、Worker 和 Webhook Processor 使用同一值。
- 轮换前先做完整数据库备份并阅读官方轮换说明。
不要把真实值写入 Compose、文章、截图或 Git。
WEBHOOK_URL
它决定外部系统看到的生产 Webhook 基础地址。GitHub、Stripe、Slack 等服务必须能从公网访问这个 HTTPS 地址。
部署后检查:
编辑器生成的生产 Webhook 是否以正式域名开头
测试路径 /webhook-test/ 与生产路径 /webhook/ 是否被混用
工作流是否已启用
反向代理是否保留 Host 与 HTTPS 协议信息
N8N_EDITOR_BASE_URL
官方文档将它定义为用户访问编辑器的公开 URL,也用于 n8n 发出的邮件和 SAML 重定向地址。即使 Webhook 正常,如果 Editor URL 没配对,登录邮件、OAuth 或管理链接仍可能指向内网地址。
反向代理:只暴露 n8n,不暴露数据库
以 Caddy 为例:
n8n.example.com {
reverse_proxy n8n:5678
}
实际部署还要检查:
- DNS 是否指向正确入口。
- HTTPS 证书是否正常续期。
- 上传大小和代理超时是否满足长任务。
- WebSocket、SSE 或流式响应是否被中断。
- 管理入口是否需要额外访问控制。
Postgres 与 Redis 应只在私有网络中可达。不要为了“方便远程连接”直接开放 5432 或 6379 到公网;需要维护时使用 SSH Tunnel、VPN 或受控跳板机。
NAS 部署:重点不是 Docker 界面,而是入口和权限
NAS 与 VPS 的差异主要在三处。
1. 没有公网 IPv4
可使用 Cloudflare Tunnel 或其他带 TLS 与访问控制的入口。不要把 NAS 管理面板、Docker Socket 或数据库端口一起暴露。
2. 挂载目录权限
先确认容器内用户是否能读写持久化目录。不要把 chmod 777 当长期解决方案;更稳的是明确目录所有者、UID/GID 和最小权限。
3. 资源有限
AI 节点、大文件解析、Code 节点和并发 Webhook 都可能占用较多内存。先设置可观测性,再决定是否限制容器资源。频繁 OOM 重启时,应减少并发、拆分大文件或迁移到更合适的主机,而不是只提高重启次数。
备份:至少保存四类资产
只备份 n8n_data 不够,也不能只做 pg_dump。
需要保存:
- Postgres 数据库。
- n8n 数据卷。
N8N_ENCRYPTION_KEY与其他 Secret 的安全副本。- Compose、反向代理和版本配置。
数据库备份示例:
docker compose exec -T postgres \
pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" \
> "backups/n8n-$(date +%F).sql"
恢复前先停止会写数据库的 n8n 实例,再把 SQL 导入空数据库。恢复完成后不要立刻宣布成功,至少验证:
- 能否登录。
- 现有 Credentials 能否解密。
- 三个关键工作流能否手动执行。
- Webhook 是否返回预期结果。
- 定时任务和时区是否正确。
没有恢复演练的备份,只能算“可能存在的文件”。
升级:先备份,再检查迁移,再回归关键工作流
推荐顺序:
- 记录当前镜像版本和数据库备份时间。
- 导出关键工作流或记录其 ID。
- 在测试实例使用目标版本启动。
- 查看 Release Notes 和 Breaking Changes。
- 回归 Webhook、OAuth、Credentials、Code 节点和 AI 节点。
- 再更新生产镜像。
- 保留可回滚的旧镜像引用和数据库备份。
不要把数据库自动迁移理解成“升级永远无风险”。自动迁移解决 Schema 变更,不替你验证第三方节点、表达式、OAuth 和业务输出。
什么时候升级 Queue Mode
先观察这些信号:
- 等待执行持续增长。
- 主实例 CPU 或内存长期饱和。
- 某个大任务拖慢编辑器与其他工作流。
- 需要让 Worker 在独立机器运行。
- 需要滚动扩容或更清晰的故障隔离。
Queue Mode 的关键约束:
EXECUTIONS_MODE=queue。- Redis 负责队列通知。
- Worker 执行实际任务。
- 主实例与 Worker 访问同一 Postgres。
- 所有实例使用同一
N8N_ENCRYPTION_KEY。 - 官方推荐 Postgres 13+,不建议 Queue Mode 使用 SQLite。
完整配置请继续阅读:n8n Queue Mode、Redis 与 Worker 实战。基础部署文只说明升级边界,不重复维护两套 Compose。
上线验收清单
[ ] n8n 和 Postgres 镜像已固定版本或 digest
[ ] .env 未提交到 Git
[ ] N8N_ENCRYPTION_KEY 已安全备份
[ ] Postgres 未映射公网端口
[ ] WEBHOOK_URL 使用正式 HTTPS 域名
[ ] N8N_EDITOR_BASE_URL 使用正式 HTTPS 域名
[ ] 反向代理能处理长请求和流式连接
[ ] EXECUTIONS_DATA_PRUNE 已按业务留存要求配置
[ ] 数据库备份已成功生成
[ ] 已在隔离环境完成一次恢复演练
[ ] 关键 Webhook、OAuth 和定时工作流已回归
常见故障
Webhook 返回 404
先区分:
- 测试 URL
/webhook-test/只在测试监听期间有效。 - 生产 URL
/webhook/要求工作流启用。 - 域名或协议错误时检查
WEBHOOK_URL和代理头。 - 路由正确但上游访问不到时检查 DNS、TLS、防火墙和 Tunnel。
凭据无法解密
最常见原因是 N8N_ENCRYPTION_KEY 变化或丢失。不要删除数据库里的 Credential 记录作为第一反应;先找回原密钥,并核对所有实例是否一致。
数据库连接失败
检查:
DB_TYPE
DB_POSTGRESDB_HOST
DB_POSTGRESDB_PORT
DB_POSTGRESDB_DATABASE
DB_POSTGRESDB_USER
数据库用户权限
Docker network
Postgres healthcheck
Worker 连接 Redis 超时
确认主实例和 Worker 使用相同 Queue 配置,Redis 地址在容器网络内可达,并且没有把 localhost 错当成另一个容器。
官方资料
继续阅读
- n8n AI Workflow 生产化门户页
- n8n Webhook 生产化实战
- n8n 错误处理、重试、超时与成本监控
- n8n Queue Mode、Redis 与 Worker 实战
- n8n Gmail 摘要写入 Google Sheets
- n8n Notion 知识库智能体
继续按 n8n 生产排障链路读
自托管、Queue Mode、Webhook、错误处理和案例文统一沉淀到 Workflow 专题页:部署文做主力页,案例文做长尾页,对比文承接工具选择流量。
下一步阅读
返回专题入口 →n8n Webhook 生产化实战:Header Auth、Raw Body、WEBHOOK_URL 与反向代理排查
系统拆解自托管 n8n Webhook 从测试到生产的关键配置,覆盖 Test URL 与 Production URL、Header Auth、JWT、Raw Body、Respond to Webhook、WEBHOOK_URL、N8N_PROXY_HOPS、反向代理、签名验签、幂等去重和安全排查。
n8n Queue Mode + Redis 实战:什么时候需要把工作流拆到队列里?
实战讲解 n8n Queue Mode、Redis 和 Worker 的生产部署设计,包括什么时候需要从 regular mode 切换到 queue mode,如何拆分 main instance、worker、webhook、Redis 和数据库,以及 AI 工作流高并发、长任务、Webhook 回调和执行超时的处理思路。
n8n Gmail 邮件摘要自动化:AI 提取待办并写入 Google Sheets
用 n8n 搭建 Gmail 邮件摘要工作流:通过 Gmail Trigger 搜索过滤邮件,调用 AI 提取摘要、优先级和待办,再按 Message ID 去重写入 Google Sheets。
n8n AI Workflow 实战:Slack 每日摘要机器人
实战讲解如何用自托管 n8n、OpenAI 和 Slack API 构建每日工作简报智能体。涵盖 Slack 消息批量拉取、短消息与系统通知过滤、多线程上下文关联,以及大模型精准决策提取与自动推送。

小白
Full-Stack AI Engineer
小白,全栈 AI 工程师,持续构建生产级 Agent 系统、产品工具与独立软件资产。
了解小白与 XBSTACK →