[控场AI]
· 9 分钟阅读· 4,574 字

n8n自托管实战:Docker+PostgreSQL+Caddy部署指南

n8n自托管实战:Docker+PostgreSQL+Caddy部署指南

自托管 n8n 的完整落地指南:三服务编排、2.x 破坏性变化、密钥与备份红线。

文章面向想把 n8n 从云端搬到自有服务器的读者,先说明自托管的三个理由——数据边界、内网访问和按资源而非执行次数计费,也点明运维成本要自己承担。接着给出 PostgreSQL、n8n、Caddy 三服务的 Compose 架构,强调 5678 只绑本机、公网走 Caddy 443。随后列出 n8n 2.x 的四处破坏性变化,拆解八步部署流程和 `.env` 中最容易踩的坑,尤其是加密密钥不可更改。最后用一个网站巡检加飞书告警的五节点流程做实战,并给出备份、升级回滚和上线后的运维约定。

为什么要自托管 n8n

按执行次数计费的云自动化服务,流程一多账单就跟着涨。自托管把订阅费换成了运维时间,换来的是数据边界、内网访问和执行量成本上的自主权。

数据边界是第一理由:实例、数据库和执行记录放在哪里由你决定。但要注意,自托管不等于数据不外发——调用飞书、大模型等外部服务时,请求内容照样会出去。

内网访问是第二理由:n8n 跑在自己的服务器上,能直接调同一内网的数据库和 NAS。第三是执行量大的成本优势:社区版按服务器资源付费,不按执行次数计费。代价是升级、备份、证书和安全加固全要自己负责。

n8n 架构示意

部署架构:三个服务,一个 Compose 文件

一个 Docker Compose 文件里放三个服务:PostgreSQL 存数据(工作流、加密后的凭证、执行记录),n8n 负责编辑器和执行引擎,Caddy 管 HTTPS。

Caddy 放在一个叫 https 的 profile 里:本地试用时不启动,上服务器时加上 --profile https,它会自动为你的域名申请和续期证书,再把请求转给 n8n。

这里有个刻意设计:n8n 的 5678 端口只绑定在 127.0.0.1,只对本机开放,公网访问一律走 Caddy 的 443,避免有人绕过 HTTPS 直接访问。也正因为前面多了一层代理,环境变量里要把 PROXY_HOPS 设成 1。

记住四个数据卷:数据库、n8n 数据、Caddy 证书和 Caddy 配置,后面备份要用到。

Docker Compose 与 profile 机制:Compose 是 Docker 官方的多容器编排工具,用一个 YAML 文件描述服务、网络和卷,docker compose up 即可按依赖顺序拉起全部容器。profile 是 Compose 的条件启用机制:标记了某个 profile 的服务默认不启动,只有命令行显式加上 --profile <名称> 时才纳入编排。这种设计让同一份配置文件能同时服务本地试用与服务器生产两种场景,避免维护两套文件。

反向代理与 PROXY_HOPS:Caddy 在这里充当反向代理,接收公网 443 端口的 HTTPS 请求再转发给内部 n8n。n8n 需要知道请求经过了几层代理才能正确还原客户端真实 IP 和协议,PROXY_HOPS 就是告诉它信任几跳 X-Forwarded-For 头。设错会导致 Webhook 地址、IP 白名单和日志记录出现偏差,前面只有 Caddy 一层时填 1。

部署前必须知道的 n8n 2.x 变化

很多老教程没写这四点。第一,原来的 Active 开关变成了 Publish 发布,导入的工作流默认没发布,定时和 Webhook 都不会生效。第二,代码执行改由任务运行器处理,以前要单独设置的 Runners 开关 2.0 起不再需要。第三,表达式和 Code 节点默认不能读取环境变量,只有把 BLOCK_ENV_ACCESS(原文变量名音近,以实际版本为准)明确设为 false 才放行。第四,官方 Compose 示例加了 AI 相关可选组件,还提醒 PostgreSQL 18 改了数据目录,今天的配置不带这些组件。

任务运行器(Task Runners):从 n8n 2.0 起,Code 节点和表达式的执行被移出主进程,交给独立的 runner 进程处理,目的是隔离不可信代码、避免阻塞主事件循环。这意味着代码执行环境与主进程的内存、环境变量默认隔离,也解释了为什么表达式默认读不到环境变量,需要显式放开访问权限。

八步部署流程

第一步,准备服务器和 Docker。个人或小团队试用两核 4G、20G 以上磁盘起步比较稳,装好 Docker Engine 和 Compose 插件后用 docker compose version 确认版本。

第二步,校准服务器时间。用 timedatectl status 确认 NTP 同步已开启,定时触发和飞书签名都依赖准确时间。

第三步,服务器正式使用时在域名服务商加一条 A 记录指向服务器 IP,安全组和防火墙放行 80 和 443,不要放行 5678。

第四步,把 Compose 文件、.env 示例文件和 Caddyfile 放到同一目录,复制出 .env 并把权限改成 600。

第五步,生成两个随机值,别手写:用 openssl rand -base64 24 生成数据库密码,用 openssl rand -hex 32 生成加密密钥。加密密钥一定要另存到密码管理器。

第六步,按场景改访问地址。本地适用保持默认;服务器正式使用就把 N8N_HOST、N8N_PROTOCOL、WEBHOOK_URL 和 PROXY_HOPS 这几行改成你的域名,模板里已写好注释。

第七步,启动前先用 docker compose config --quiet 校验配置,注意它只做静态校验,不能证明密码对、镜像能拉、数据库能连。本地适用执行 docker compose up -d,服务器正式使用再加 --profile https 把 Caddy 一起拉起来。

第八步,验证。先看 docker compose ps,再分别请求两个健康检查接口:liveness 只说明进程活着,readiness 才会检查数据库连接,首次启动要等迁移完成。然后打开编辑器,本地打开 localhost:5678,远程服务器等域名和证书好了打开 HTTPS 域名,创建 Owner 账号、设强密码、开两步验证。

Docker Compose 启动验证

环境变量里最要命的几个坑

.env 文件只有十几个变量,但每个都对应一个真实的坑。

WEBHOOK_URL 不设的话,Webhook 节点显示的地址会变成 localhost,外部系统根本调不进来。GENERIC_TIMEZONE 设成 Asia/Shanghai,不然定时任务差 8 小时。N8N_VERSION 固定版本号,用 latest 可能在你不知情时升级还顺带迁移了数据库。执行记录清理的两个变量保留 336 小时、最多 1 万条,防止数据库一直膨胀。

最要命的是加密密钥。实验过:先用一个密钥启动,再换一个不同的密钥启动,n8n 直接拒绝运行,报错 Mismatching Encryption Keys。密钥一旦定下来就别改,重装服务器时要连同备份一起恢复而不是重新生成。密钥真丢了,已保存的凭证就解不开,只能逐个重新填。

凭证加密机制:n8n 用 N8N_ENCRYPTION_KEY 对数据库中的凭证(如飞书机器人地址、API Key)做对称加密后再落盘,因此密钥是解密这些数据的唯一凭据。密钥不随数据库导出而备份,必须单独保存;恢复时数据库和密钥必须成对还原,否则凭证字段只是一堆无法还原的密文,只能逐条重新填写。

第一个实用流程:网站巡检加飞书告警

选它是因为人人用得上,而且不用申请任何 API Key,只要一个飞书群机器人。整个流程五个节点、两个入口:手动运行用来测试,定时触发射程每 30 分钟一次。

检查网站用 HTTP 请求节点 GET 你的网址,打开返回完整响应、不因错误状态码报错、超时 10 秒,并把节点设成出错时继续执行,这样连接失败也能走到下一步。接着是 IF 判断状态码不等于 200 就走真分支,最后只在真分支上接飞书通知,消息里带状态码、错误信息和时间。

为什么不在两个分支都发?正常时也推送很快就变成噪音,大家就不看了。告警类流程只在异常时说话。

HTTP 请求节点返回完整响应

具体操作六步:先在飞书群设置里添加自定义机器人,复制 webhook 地址,安全设置选自定义关键词填“网站”两个字;然后在 n8n 编辑器右上角选 Import from file 导入工作流文件,把检查网站节点里的网址和飞书节点里的机器人地址换成你自己的;点 Execute Workflow,网站正常时 IF 的假分支输出,飞书群收不到消息,这是预期行为;接着故意制造故障,把网址改成不存在的路径,至少测三种情况——返回 200 不告警、返回 503 告警、连接被拒绝也告警;最后改回网址点 Publish,发布后定时才生效,Executions 页面每 30 分钟会多一条记录。

还要提醒一句:飞书节点亮绿钩不等于告警送到了,要同时看接口返回的业务响应和群里是不是真的收到了消息。

备份、升级与回滚

上线前先把备份做了,至少备份三样:PostgreSQL 数据库、n8n 数据卷以及 .env 文件。数据库用 pg_dump 导出放进 cron,每天跑再拷到另一台机器或对象存储。注意导出失败时也可能留下空文件,文件存在不代表备份成功,要看命令退出状态和错误输出。

最稳妥的做法是在隔离环境里真恢复一次,把定时和外发通知都关掉,核对工作流和凭证能正常解密。

在隔离环境验证恢复

升级固定五步:读目标版本的发布说明,重点看破坏性变更;备份数据库;改 .env 里的版本号;只拉取并重建 n8n 这一个服务;最后看日志确认迁移完成、readiness 返回正常,再抽查两三个关键工作流。

回滚要格外小心:升级时执行过的数据库迁移不会自动撤销,想退回旧版本必须先用升级前的备份恢复数据库,再改回旧版本号。

上线后的几条约定

部署好的 n8n 真正产生价值,是在你把日报、采集、通知这些重复工作接进来之后。接之前先统一几条约定:接口返回要主动检查,非书返回码不是 0、微信返回 Rcode 不是 0 就让执行失败,别让它看起来成功实际没写进去;再做一个全局错误工作流,用 Error Trigger 节点,在每个工作流的设置里选它,出错就告警,注意错误工作流本身也必须发布;采集类流程要做跨执行去重,但去重节点参数写错不会报错,改过的去重逻辑要留意,不要连跑两次确认第二次没有新数据通过。

安全上,.env 权限 600,不要提交到 Git;对外的 Webhook 至少加口令校验;凡是会发消息、写数据的流程,先用测试群测试表验证;用大模型写日报文案的,对外发布前一定人工确认。

总结:自托管 n8n 必须配好的四件事

Webhook 的 URL、加密密钥、时区、数据库备份——这四样配错,后面的工作全白搭。先在本机跑通再上服务器,固定版本号,升级前必须先备份。

#n8n 自托管#Docker Compose#PostgreSQL#Caddy#HTTPS#n8n 教程#飞书告警#工作流自动化
分享:

相关推荐