如何把n8n部署到线上:vps篇
给小白的 n8n VPS 部署教程:从域名、Docker Compose、PostgreSQL 与 HTTPS 开始,每一步都解释用途、成功标志、与教程不一致时的处理方式,以及最后的完整排障清单。
当你需要让 n8n 在电脑关机后继续运行、稳定接收 Webhook、每天按时执行任务时,就该考虑 VPS。VPS 可以理解为“放在数据中心、一直联网的一台 Linux 小电脑”。你租用它的计算和网络资源,然后在上面运行自己的 n8n。
本文会带你搭建一个最小但完整的线上架构:域名负责让人找到你的服务,反向代理负责 HTTPS,n8n 容器负责自动化,PostgreSQL 负责保存数据,备份负责在出错时救回来。
本文不是让你一次记住所有名词。每一步先做最小操作,再解释它做了什么、看到什么算成功、如果和教程不同该怎么处理。
先看最终会搭出什么

| 组件 | 小白解释 | 缺少它会怎样 |
|---|---|---|
| VPS | 一台 24 小时开机的远程电脑 | 任务仍会依赖你的个人电脑 |
| Docker Compose | 一份“同时启动几个服务”的说明书 | 每个服务要手动配置,容易遗漏 |
| PostgreSQL | 存工作流和执行记录的数据库 | 长期线上运行不够稳妥 |
| Caddy | 自动处理 HTTPS 的门卫 | 外部服务难以安全访问 Webhook |
| volume | 容器外面的持久化硬盘空间 | 重建容器后数据可能丢失 |
| 备份 | 出错时的后悔药 | 误删、硬盘故障或升级失败无法恢复 |
n8n 官方将 Docker Compose 作为适合包含数据库和附加服务的生产部署选择之一。n8n 自托管指南
第 0 步:开始前准备什么
你需要一台 Linux VPS、一个可以管理 DNS 的域名、SSH 登录权限,以及半小时到一小时的安静时间。建议先准备子域名,例如 n8n.example.com,不要把 n8n 管理界面直接放在主站域名下。
你暂时不需要: Kubernetes、复杂监控、多个服务器或高可用集群。先把一个可恢复的单机环境跑稳,比一开始堆很多组件更重要。
第 1 步:让域名指向 VPS
在 DNS 服务商后台新增一条 A 记录:主机名填写 n8n,记录值填写 VPS 的 IPv4 地址。保存后等待解析生效。
这一步在做什么? 把 n8n.example.com 翻译成 VPS 的 IP。之后 Caddy 才能为这个域名申请 HTTPS 证书。
成功标志: 在本地终端执行 nslookup n8n.example.com 或使用任意 DNS 查询工具,结果显示为你的 VPS IP。
如果和教程不同: 有些 DNS 面板叫“记录名称”“Host”或“子域名”,含义相同;如果你使用 Cloudflare,先确保 DNS 记录正确,再处理代理模式和证书配置。
第 2 步:登录服务器并准备目录
用 SSH 登录 VPS。创建一个专门放部署文件和备份的目录:
mkdir -p ~/n8n-production/backups
cd ~/n8n-production
这一步在做什么? 以后 compose.yaml、.env 和备份都会放在这里。目录固定,升级和排查时不会找不到文件。
接下来安装 Docker Engine 和 Docker Compose 插件。不同 Linux 发行版的安装命令不同,请优先使用 Docker 官方安装文档。安装完成后检查:
docker version
docker compose version
两个命令都能显示版本号,就说明 Docker 已准备好。
第 3 步:创建 .env,把密码和配置分开保存
.env 是一个只存变量的文件。不要把密码直接写在 compose.yaml 中,更不要上传到 GitHub。
N8N_HOST=n8n.example.com
N8N_ENCRYPTION_KEY=<一串长期保存的随机密钥>
POSTGRES_DB=n8n
POSTGRES_USER=n8n
POSTGRES_PASSWORD=<强随机数据库密码>
生成随机密钥可执行:
openssl rand -hex 32
chmod 600 .env
这一步在做什么? N8N_ENCRYPTION_KEY 是 n8n 保护凭据的总钥匙;POSTGRES_PASSWORD 是数据库密码。chmod 600 让同一台服务器上的其他普通用户不能读取该文件。
成功标志: ls -l .env 显示该文件只允许当前用户读写。
非常重要: 换服务器、恢复备份或升级时,要保留原来的 N8N_ENCRYPTION_KEY。否则之前保存的 API Key、OAuth 凭据可能无法解密。
第 4 步:创建 Compose 配置
新建 compose.yaml,将下面内容复制进去。它会启动三个服务:PostgreSQL、n8n 和 Caddy。PostgreSQL 不直接暴露端口给公网,只有 n8n 能在内部网络里访问它。
services:
postgres:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks: [n8n_internal]
n8n:
image: docker.n8n.io/n8nio/n8n:stable
restart: unless-stopped
depends_on: [postgres]
environment:
N8N_HOST: ${N8N_HOST}
N8N_PROTOCOL: https
N8N_PORT: 5678
N8N_WEBHOOK_URL: https://${N8N_HOST}/
N8N_PROXY_HOPS: 1
GENERIC_TIMEZONE: Asia/Shanghai
TZ: Asia/Shanghai
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
networks: [n8n_internal]
caddy:
image: caddy:2-alpine
restart: unless-stopped
depends_on: [n8n]
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks: [n8n_internal]
volumes:
postgres_data:
n8n_data:
caddy_data:
caddy_config:
networks:
n8n_internal:
n8n 默认使用 SQLite,但官方支持通过 DB_TYPE=postgresdb 与 DB_POSTGRESDB_* 变量连接 PostgreSQL。n8n 数据库变量参考 即使已使用 PostgreSQL,也建议保留 /home/node/.n8n 的 volume,因为这里仍包含加密密钥、日志等重要资产。n8n Docker 文档
第 5 步:创建 HTTPS 反向代理配置
新建 Caddyfile,把域名换成你自己的:
n8n.example.com {
reverse_proxy n8n:5678
}
这一步在做什么? Caddy 接收公网的 HTTPS 请求,再安全地转发给内部的 n8n。外部只需要访问 443 端口,n8n 的 5678 端口不暴露在互联网。
n8n 位于反向代理后时,应设置 N8N_WEBHOOK_URL 和 N8N_PROXY_HOPS=1,让它生成正确的公网 Webhook 地址。n8n 反向代理与 Webhook 指南
第 6 步:先检查,再启动
先运行:
docker compose config
成功标志: 命令没有报错,并输出整理后的配置。它相当于启动前的语法检查。
再启动:
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 n8n
docker compose ps 中三个服务都显示 running 或 up,说明容器已启动。然后打开 https://n8n.example.com 创建 n8n owner 账户。
第 7 步:第一次上线后必须做的三个测试
- 手动测试。 创建 Manual Trigger → Set,执行成功说明 n8n 基础可用。
- Webhook 测试。 创建一个测试 Webhook,从手机流量或外部 HTTP 工具访问它,确认不是只在服务器内部可用。
- 定时测试。 建一个短时间间隔的测试任务,确认时区正确、日志有执行记录。
如果时间偏差八小时,优先检查 GENERIC_TIMEZONE 与 TZ;如果 Webhook 显示内部地址,检查域名、N8N_WEBHOOK_URL 和 Caddy。
跟教程不同怎么办:按这个顺序排查
| 你看到的情况 | 不要急着做什么 | 应该先做什么 |
|---|---|---|
| 域名打不开 | 不要反复重启容器 | 检查 DNS 是否指向 VPS、80/443 是否开放 |
| Caddy 申请证书失败 | 不要关闭 HTTPS | 检查域名解析、端口、防火墙和是否被其他服务占用 |
| n8n 一直重启 | 不要删除 volume | 查看 docker compose logs n8n 和 postgres 日志 |
| 数据库报认证失败 | 不要随意换密钥 | 检查 .env 密码、变量是否被 Compose 正确读取 |
| 升级后流程异常 | 不要先执行 down -v |
停止写入、保留日志、按备份与版本记录回滚 |
备份:把“能恢复”当成目标
volume 能防止容器重建时丢数据,但不是备份。至少要保存三类东西:PostgreSQL 数据库、.env 里的加密密钥和密码、n8n_data 中的重要文件。
数据库备份示例:
mkdir -p backups
set -a && source .env && set +a
docker compose exec -T postgres \
pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
> "backups/n8n-$(date +%F).sql"
成功标志: backups 目录出现非零大小的 .sql 文件。更进一步,请在单独环境做一次恢复演练;只有真正恢复成功,备份才算有效。
常见问题与排障汇总
| 问题 | 原因解释 | 解决顺序 |
|---|---|---|
| 浏览器出现 502/Bad Gateway | Caddy 找不到 n8n,或 n8n 未启动 | docker compose ps → docker compose logs n8n → 检查 Caddyfile |
| Docker 提示端口已被占用 | 80、443 或 5678 被其他程序使用 | sudo ss -ltnp 查占用;不要同时运行两套代理 |
| n8n 能打开但第三方回调失败 | 公开 URL、HTTPS 或 Webhook 地址错误 | 用外网测试 URL → 检查 N8N_WEBHOOK_URL → 查看代理日志 |
| 重启后数据不见 | 没有使用 volume 或误删 volume | 检查 docker volume ls;恢复备份;以后不要随意 down -v |
| 凭据全部失效 | 加密密钥变了 | 恢复原 .env 中的 N8N_ENCRYPTION_KEY |
| 升级后工作流报错 | n8n 版本变化或节点行为变化 | 阅读变更说明、用测试工作流验证、必要时回滚镜像 |
| 服务器磁盘越来越满 | 执行记录、日志或备份没有轮换 | 定期检查 df -h,清理过期备份,设置执行数据保留策略 |
小结
VPS 的价值不只是“让 n8n 在线”,而是让运行、数据、密钥和备份都有明确位置。只要你坚持每次升级前备份、不要暴露内部端口、保管好加密密钥,并在出现问题时先看日志而不是先删数据,这套架构足够服务大多数个人与小团队的长期自动化。