Self-hosted n8n Docker Compose、Postgres、VPS 与 NAS 生产部署指南 - XBSTACK

Self-hosted n8n 部署指南:Docker Compose、Postgres、VPS 与 NAS 生产基线

Release Date
2026-05-28
Reading Time
8分钟
Content Size
3,727 chars
n8n
workflow-automation
self-hosted
docker
postgres
Xiaobai's Note / 实验室笔记

这篇文章记录了我在贵阳实验室的实战过程。我坚信,在技术下行的时代,程序员唯一的护城河就是通过 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不需要不需要
个人长期运行、定时任务、WebhookPostgresVPS 反代或受控 Tunnel暂不需要
NAS 内网部署、无公网 IPPostgresCloudflare Tunnel / 受控反代暂不需要
多人使用、执行量稳定增长Postgres正式域名 + HTTPS观察队列和资源再决定
多 Worker、横向扩容Postgres正式域名 + HTTPS需要 Redis + Queue Mode

n8n 官方文档说明,自托管默认使用 SQLite,也支持 Postgres;Queue Mode 官方建议使用 Postgres,并明确不推荐与 SQLite 组合。数据库选择不是“SQLite 一定会坏”,而是要看持久运行、扩展和恢复要求。

部署前先准备四样东西

  1. 一个固定域名,例如 n8n.example.com
  2. 一个不会随容器重建而变化的 N8N_ENCRYPTION_KEY
  3. 独立保存的数据库密码,不写进 Git 仓库。
  4. 明确的镜像版本或 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_IMAGEPOSTGRES_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 应只在私有网络中可达。不要为了“方便远程连接”直接开放 54326379 到公网;需要维护时使用 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

需要保存:

  1. Postgres 数据库。
  2. n8n 数据卷。
  3. N8N_ENCRYPTION_KEY 与其他 Secret 的安全副本。
  4. Compose、反向代理和版本配置。

数据库备份示例:

docker compose exec -T postgres \
  pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" \
  > "backups/n8n-$(date +%F).sql"

恢复前先停止会写数据库的 n8n 实例,再把 SQL 导入空数据库。恢复完成后不要立刻宣布成功,至少验证:

  • 能否登录。
  • 现有 Credentials 能否解密。
  • 三个关键工作流能否手动执行。
  • Webhook 是否返回预期结果。
  • 定时任务和时区是否正确。

没有恢复演练的备份,只能算“可能存在的文件”。

升级:先备份,再检查迁移,再回归关键工作流

推荐顺序:

  1. 记录当前镜像版本和数据库备份时间。
  2. 导出关键工作流或记录其 ID。
  3. 在测试实例使用目标版本启动。
  4. 查看 Release Notes 和 Breaking Changes。
  5. 回归 Webhook、OAuth、Credentials、Code 节点和 AI 节点。
  6. 再更新生产镜像。
  7. 保留可回滚的旧镜像引用和数据库备份。

不要把数据库自动迁移理解成“升级永远无风险”。自动迁移解决 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 错当成另一个容器。

官方资料

继续阅读

专题入口 / AI Workflow Hub

继续按 n8n 生产排障链路读

自托管、Queue Mode、Webhook、错误处理和案例文统一沉淀到 Workflow 专题页:部署文做主力页,案例文做长尾页,对比文承接工具选择流量。

下一步阅读

返回专题入口 →
小白

小白

Full-Stack AI Engineer

小白,全栈 AI 工程师,持续构建生产级 Agent 系统、产品工具与独立软件资产。

了解小白与 XBSTACK →

喜欢这篇文章?
加入小白实验室的周刊

每期只整理 AI 工程变化、真实故障、可复现实验、值得尝试的工具和 XBSTACK 新资产,不做泛新闻汇总,也不为周更凑数。

Comments