生产环境中备份和更新n8n(Docker Compose + PostgreSQL)
自托管n8n的日常运维指南:自动化PostgreSQL备份、加密密钥保护、使用rclone进行异地备份、从全新VPS进行灾难恢复、安全的Docker Compose更新、回滚以及1.x到2.x的迁移路径。
你已经在VPS上用Docker Compose和PostgreSQL跑起了n8n。这是第一天的事。第二天要做的是让它持续运行:自动化备份、经过测试的恢复流程和安全的更新操作。
本指南涵盖保护生产环境n8n实例所需的一切。我们会构建一个备份脚本,用cron自动化执行,将备份复制到服务器外,在全新VPS上完成灾难恢复,并安全更新n8n(支持回滚)。我们还会介绍1.x到2.x的迁移路径。
本指南假设你已按照我们的n8n Docker Compose安装指南完成了n8n的部署。你的技术栈使用Docker Compose搭配PostgreSQL,有一个非root的deploy用户和反向代理。
n8n存储了哪些需要备份的数据?
n8n存储五个组件,必须一起备份。缺少任何一个都可能导致恢复不完整或无法恢复。PostgreSQL数据库保存你的工作流、凭据(加密的)、执行历史和用户账户。加密密钥(encryption key)用于解密这些凭据。Docker卷存储自定义节点和二进制数据。.env和docker-compose.yml文件定义运行时配置。
| 组件 | 内容 | 丢失风险 | 备份方法 |
|---|---|---|---|
| PostgreSQL数据库 | 工作流、加密凭据、执行历史、用户 | 所有数据丢失 | pg_dump(custom格式) |
N8N_ENCRYPTION_KEY |
用于凭据解密的AES-256密钥 | 所有保存的凭据永久不可恢复 | 从.env或容器配置中复制 |
Docker卷(.n8n) |
自定义节点、二进制执行数据 | 自定义节点和上传文件丢失 | Alpine tar容器 |
.env文件 |
数据库密码、加密密钥、域名配置 | 需手动重建 | 文件复制 |
docker-compose.yml |
服务定义、卷映射、镜像标签 | 需手动重建 | 文件复制 |
如何用pg_dump备份n8n的PostgreSQL数据库?
使用pg_dump配合--format=custom创建n8n数据库的压缩可恢复转储。custom格式支持选择性恢复和并行处理。通过运行中的PostgreSQL容器执行转储。
docker exec n8n-postgres \
pg_dump -U n8n -d n8n --format=custom \
> /home/deploy/n8n-backups/n8n-db-$(date +%Y%m%d-%H%M%S).dump
这个命令做了什么: docker exec在PostgreSQL容器内执行pg_dump。-U n8n是数据库用户。-d n8n是数据库名称。--format=custom生成压缩的二进制转储,pg_restore可以选择性恢复。输出重定向到主机上带时间戳的文件。
验证转储是否有效:
cat /home/deploy/n8n-backups/n8n-db-*.dump | docker exec -i n8n-postgres pg_restore --list | head -20
因为pg_restore在PostgreSQL容器内,所以通过管道将转储传给docker exec -i。你应该看到数据库对象列表(表、序列、数据)。如果看到错误或空输出,说明转储失败了。
备份Docker卷
n8n数据卷包含自定义节点和二进制执行数据。用临时Alpine容器备份:
docker run --rm \
-v n8n_n8n_data:/source:ro \
-v /home/deploy/n8n-backups:/backup \
alpine tar czf "/backup/n8n-data-$(date +%Y%m%d-%H%M%S).tar.gz" -C /source .
卷名n8n_n8n_data由Docker Compose项目名(n8n,来自目录名)加上卷名(n8n_data,来自docker-compose.yml)组成。如果你的不同,运行docker volume ls查看。
这个命令做了什么: 将n8n数据卷以只读方式(:ro)挂载到一次性Alpine容器中。容器创建卷内容的压缩归档并写入备份目录。容器完成后自动删除(--rm)。
验证归档:
tar tzf /home/deploy/n8n-backups/n8n-data-*.tar.gz | head -10
你应该看到.n8n目录下的文件路径。
备份配置文件
BACKUP_DIR="/home/deploy/n8n-backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
cp /home/deploy/n8n/.env "$BACKUP_DIR/env-$TIMESTAMP.bak"
cp /home/deploy/n8n/docker-compose.yml "$BACKUP_DIR/docker-compose-$TIMESTAMP.yml.bak"
chmod 600 "$BACKUP_DIR/env-$TIMESTAMP.bak"
.env文件包含你的N8N_ENCRYPTION_KEY和数据库密码。将权限限制为600,只有备份用户可以读取。
为什么n8n加密密钥是最重要的备份?
n8n使用N8N_ENCRYPTION_KEY以AES-256加密每个保存的凭据(API密钥、OAuth令牌、数据库密码)。如果丢失这个密钥,数据库中的每个凭据都将永久不可恢复。没有重置机制,没有后门。你必须在每个工作流中手动重新输入每个凭据。
密钥要么在.env文件中显式设置,要么由n8n首次启动时自动生成并存储在容器内的/home/node/.n8n/config中。
查找你的加密密钥:
如果你在.env中设置了它:
grep N8N_ENCRYPTION_KEY /home/deploy/n8n/.env
如果n8n自动生成了它(你从未显式设置):
docker exec n8n cat /home/node/.n8n/config
在JSON输出中查找encryptionKey字段。
安全存储密钥:
- 将密钥值复制到密码管理器(Bitwarden、KeePass、1Password)。
- 确保它以
N8N_ENCRYPTION_KEY=<你的密钥>的形式存在于.env文件中。 - 备份脚本已经会复制
.env,但也要单独存储密钥。如果服务器报废且备份损坏,只要有密钥就能重新创建凭据。
如何用cron自动化n8n备份?
将数据库转储、卷备份和配置复制合并到一个脚本中。添加保留期清理和健康检查ping来捕获失败。
创建备份脚本:
nano /home/deploy/n8n/backup-n8n.sh
#!/usr/bin/env bash
set -euo pipefail
# --- Configuration ---
BACKUP_DIR="/home/deploy/n8n-backups"
N8N_DIR="/home/deploy/n8n"
COMPOSE_PROJECT="n8n"
DB_CONTAINER="n8n-postgres" # Matches container_name in docker-compose.yml
DB_USER="n8n"
DB_NAME="n8n"
VOLUME_NAME="${COMPOSE_PROJECT}_n8n_data" # docker compose prefixes project name
RETENTION_DAYS=7
RETENTION_WEEKS=4
HEALTHCHECK_URL="" # Set to your healthcheck.io or uptime-kuma URL
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
DAY_OF_WEEK=$(date +%u)
mkdir -p "$BACKUP_DIR/daily" "$BACKUP_DIR/weekly"
# --- PostgreSQL dump ---
echo "[$(date)] Starting PostgreSQL backup..."
docker exec "$DB_CONTAINER" \
pg_dump -U "$DB_USER" -d "$DB_NAME" --format=custom \
> "$BACKUP_DIR/daily/n8n-db-$TIMESTAMP.dump"
# Verify dump is not empty
if [ ! -s "$BACKUP_DIR/daily/n8n-db-$TIMESTAMP.dump" ]; then
echo "[$(date)] ERROR: Database dump is empty!" >&2
exit 1
fi
# --- Docker volume backup ---
echo "[$(date)] Starting volume backup..."
docker run --rm \
-v "${VOLUME_NAME}:/source:ro" \
-v "$BACKUP_DIR/daily:/backup" \
alpine tar czf "/backup/n8n-data-$TIMESTAMP.tar.gz" -C /source .
# --- Config files ---
echo "[$(date)] Backing up config files..."
cp "$N8N_DIR/.env" "$BACKUP_DIR/daily/env-$TIMESTAMP.bak"
cp "$N8N_DIR/docker-compose.yml" "$BACKUP_DIR/daily/docker-compose-$TIMESTAMP.yml.bak"
chmod 600 "$BACKUP_DIR/daily/env-$TIMESTAMP.bak"
# --- Weekly copy (Sundays) ---
if [ "$DAY_OF_WEEK" -eq 7 ]; then
echo "[$(date)] Creating weekly backup copy..."
cp "$BACKUP_DIR/daily/n8n-db-$TIMESTAMP.dump" "$BACKUP_DIR/weekly/"
cp "$BACKUP_DIR/daily/n8n-data-$TIMESTAMP.tar.gz" "$BACKUP_DIR/weekly/"
cp "$BACKUP_DIR/daily/env-$TIMESTAMP.bak" "$BACKUP_DIR/weekly/"
fi
# --- Retention cleanup ---
echo "[$(date)] Cleaning old backups..."
find "$BACKUP_DIR/daily" -type f -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR/weekly" -type f -mtime +$((RETENTION_WEEKS * 7)) -delete
# --- Health check ping ---
if [ -n "$HEALTHCHECK_URL" ]; then
curl -fsS --retry 3 "$HEALTHCHECK_URL" > /dev/null
fi
echo "[$(date)] Backup complete."
设置权限并测试:
chmod 750 /home/deploy/n8n/backup-n8n.sh
/home/deploy/n8n/backup-n8n.sh
验证备份文件是否已创建:
ls -lh /home/deploy/n8n-backups/daily/
注意文件大小。一个典型的n8n数据库转储在500 KB到50 MB之间,取决于执行历史。如果转储为0字节,说明出了问题。
用cron调度
crontab -e
添加这一行,每天03:00运行备份:
0 3 * * * /home/deploy/n8n/backup-n8n.sh >> /home/deploy/n8n-backups/backup.log 2>&1
这个命令做了什么: 每天凌晨3点运行备份脚本。输出和错误追加到backup.log以便排查故障。
备份失败告警
脚本中的HEALTHCHECK_URL变量支持Healthchecks.io或自托管的Uptime Kuma实例等服务。这些服务期望定期收到ping。如果ping停止(因为脚本失败或cron未执行),你会收到告警。
设置一个24小时周期、1小时宽限期的检查。如果备份脚本在04:00前没有ping,你就会收到通知。
如何用rclone将n8n备份复制到服务器外?
备份在n8n同一台服务器上不算真正的备份。如果磁盘故障,两者都会丢失。使用rclone将备份复制到S3兼容的对象存储(Wasabi、Backblaze B2、MinIO或任何S3提供商)。
安装rclone:
sudo apt install rclone
配置远程存储。此示例使用任何S3兼容存储:
rclone config
按照交互提示创建名为n8n-backup的远程。选择"Amazon S3 Compliant Storage Providers"并输入你的endpoint、访问密钥和秘密密钥。
验证远程是否工作:
rclone lsd n8n-backup:
你应该看到你的bucket列表。
将同步添加到备份脚本中。在health check ping行之前插入:
# --- Off-server copy ---
echo "[$(date)] Syncing to remote storage..."
rclone sync "$BACKUP_DIR" n8n-backup:your-bucket-name/n8n \
--transfers 4 \
--min-age 1m \
--log-level ERROR
--min-age 1m避免上传仍在写入的文件。--transfers 4运行四个并行上传。
如何验证n8n备份是否有效?
从未测试过的备份等于没有备份。定期运行这些检查。
数据库转储完整性:
cat /home/deploy/n8n-backups/daily/n8n-db-*.dump | docker exec -i n8n-postgres pg_restore --list > /dev/null 2>&1 && echo "OK" || echo "CORRUPT"
卷归档完整性:
tar tzf /home/deploy/n8n-backups/daily/n8n-data-*.tar.gz > /dev/null 2>&1 && echo "OK" || echo "CORRUPT"
用于远程验证的校验和:
备份后生成校验和:
sha256sum /home/deploy/n8n-backups/daily/* > /home/deploy/n8n-backups/daily/checksums.sha256
从远程存储下载后验证:
sha256sum -c checksums.sha256
每行应显示OK。任何不匹配意味着文件在传输过程中损坏了。
如何在全新VPS上从备份恢复n8n?
这是灾难恢复流程。你的服务器丢失了,你有一台全新VPS和备份文件(来自远程存储或本地副本)。以下是让n8n重新运行的步骤。
1. 配置新VPS并安装Docker
设置一台安装了Docker和Docker Compose的VPS。按照我们的n8n Docker Compose安装指南操作到Docker安装步骤,然后回到这里。
2. 创建deploy用户和项目目录
adduser --disabled-password deploy
mkdir -p /home/deploy/n8n /home/deploy/n8n-backups
chown deploy:deploy /home/deploy/n8n /home/deploy/n8n-backups
3. 下载备份
从远程存储:
su - deploy
rclone copy n8n-backup:your-bucket-name/n8n/daily /home/deploy/n8n-backups/daily --progress
或从另一台服务器通过scp:
scp user@old-server:/home/deploy/n8n-backups/daily/* /home/deploy/n8n-backups/daily/
4. 恢复配置文件
cp /home/deploy/n8n-backups/daily/env-*.bak /home/deploy/n8n/.env
cp /home/deploy/n8n-backups/daily/docker-compose-*.yml.bak /home/deploy/n8n/docker-compose.yml
chmod 600 /home/deploy/n8n/.env
验证加密密钥存在:
grep N8N_ENCRYPTION_KEY /home/deploy/n8n/.env
这个命令必须返回包含你密钥的一行。如果为空或缺失,从密码管理器中找到密钥并手动添加。没有这个密钥,你的凭据就丢了。
5. 只启动PostgreSQL
cd /home/deploy/n8n
docker compose up -d postgres
等待PostgreSQL初始化:
docker compose logs -f postgres
找到database system is ready to accept connections。
6. 恢复数据库
docker cp /home/deploy/n8n-backups/daily/n8n-db-*.dump n8n-postgres:/tmp/n8n.dump
docker exec n8n-postgres pg_restore -U n8n -d n8n --clean --if-exists /tmp/n8n.dump
这个命令做了什么: 将转储文件复制到容器内,然后pg_restore加载它。--clean在恢复前删除现有对象。--if-exists防止对象不存在时报错。
你可能会看到一些关于对象不存在的警告。这在全新数据库上是正常的。关于数据或约束的错误则不正常。
7. 恢复Docker卷
Docker Compose在上一步启动PostgreSQL时已创建了n8n_n8n_data卷。恢复到其中:
docker run --rm \
-v n8n_n8n_data:/target \
-v /home/deploy/n8n-backups/daily:/backup:ro \
alpine sh -c "tar xzf /backup/n8n-data-*.tar.gz -C /target"
8. 启动完整技术栈
docker compose up -d
9. 验证恢复
docker compose ps
所有容器应显示running状态。
检查n8n日志:
docker compose logs -f n8n
找到Editor is now accessible via: http://localhost:5678。不应有加密密钥错误。
打开n8n Web界面并验证:
- 工作流存在且与之前一致。
- 凭据可用。 打开任意凭据确认显示存储的值(API密钥、密码)。如果看到空字段或解密错误,说明加密密钥不对。
- 运行一个测试工作流。 执行一个简单工作流确认数据库连接和执行引擎正常。
从服务器外部测试:
curl -s https://your-n8n-domain.com/healthz
200响应确认n8n可访问且正在运行。
如何用Docker Compose安全更新n8n?
更新n8n遵循四个步骤:备份、检查发行说明、拉取新镜像、验证。永远不要跳过备份步骤。
1. 运行备份
/home/deploy/n8n/backup-n8n.sh
这是必须的。如果更新出问题,你需要一个恢复点。
2. 检查发行说明中的破坏性变更
拉取新版本前,阅读n8n发行说明。注意:
- 数据库迁移(启动时自动运行但可能耗时)
- 你的工作流使用的已弃用节点
- 变更的环境变量
- PostgreSQL最低版本要求
3. 记录当前版本
docker compose exec n8n n8n --version
记下来。回滚时需要。
4. 拉取并重启
如果你的docker-compose.yml使用n8nio/n8n:latest或n8nio/n8n:stable标签:
cd /home/deploy/n8n
docker compose pull
docker compose down
docker compose up -d
如果你锁定了特定版本(生产环境推荐),先编辑docker-compose.yml:
services:
n8n:
image: n8nio/n8n:2.12.3 # Change to the target version
然后:
docker compose up -d
5. 验证更新
docker compose exec n8n n8n --version
确认版本与预期一致。
检查日志中的迁移错误:
docker compose logs --tail 50 n8n
找到Migrations completed和Editor is now accessible via: http://localhost:5678。
测试API健康端点:
curl -s https://your-n8n-domain.com/healthz
通过界面运行一个测试工作流确认执行正常。
如何在n8n更新失败后回滚?
如果更新破坏了工作流或界面不可访问,回退到前一版本。
1. 停止故障实例
cd /home/deploy/n8n
docker compose down
2. 设置之前的镜像标签
编辑docker-compose.yml,将镜像改为之前的版本:
services:
n8n:
image: n8nio/n8n:2.11.2 # Your previous working version
3. 如果迁移已运行则恢复数据库
如果n8n在失败的更新中运行了数据库迁移,schema可能已改变。从更新前的备份恢复:
docker compose up -d postgres
docker cp /home/deploy/n8n-backups/daily/n8n-db-*.dump n8n-postgres:/tmp/n8n.dump
docker exec n8n-postgres pg_restore -U n8n -d n8n --clean --if-exists /tmp/n8n.dump
4. 启动旧版本
docker compose up -d
验证:
docker compose exec n8n n8n --version
docker compose logs --tail 20 n8n
这就是我们每次更新前备份的原因。没有更新前的转储,迁移后就无法安全降级。
如何将n8n从1.x迁移到2.x?
n8n 2.0于2025年12月发布,包含破坏性变更。如果你还在1.x,尽快迁移。1.x版本在2.0发布后仅提供了三个月的安全修复,该窗口现已关闭。
n8n 2.0中的主要破坏性变更
| 变更 | 1.x行为 | 2.x行为 | 需要的操作 |
|---|---|---|---|
| MySQL/MariaDB支持 | 支持 | 完全移除 | 升级前迁移到PostgreSQL |
| Task runners | 可选 | 默认启用,独立Docker镜像 | 外部模式使用n8nio/runners镜像 |
| Code node环境变量 | 可访问 | 默认阻止 | 如需要设置N8N_BLOCK_ENV_ACCESS_IN_NODE=false |
| Start node | 可用 | 移除 | 替换为特定触发器节点 |
| Save vs. Publish | Save = deploy | Save和Publish是分开的操作 | 更新团队工作流 |
| Python Code node | 基于Pyodide | 通过task runners使用原生Python | 使用外部模式task runners |
ExecuteCommand和LocalFileTrigger节点 |
启用 | 默认禁用 | 如需要则显式启用 |
--tunnel CLI选项 |
可用 | 移除 | 改用反向代理 |
N8N_CONFIG_FILES |
支持 | 移除 | 直接使用环境变量 |
步骤1:运行迁移报告
迁移报告工具在n8n 1.121.0及更高版本中可用。它在升级前识别工作流级别和实例级别的问题。
在n8n界面中,进入Settings > Migration Report。此选项仅对全局管理员可见。
报告有两个标签页:
- Workflow Issues: 列出使用已弃用节点、已移除功能或行为变更的工作流。
- Instance Issues: 标记需要更新的环境变量和配置。
继续之前修复报告识别出的每个问题。
步骤2:先更新到最新的1.x
在跳到2.x之前,先更新到最新的1.x版本。这确保所有中间数据库迁移都能运行:
docker compose pull # With image set to n8nio/n8n:1-latest or latest 1.x tag
docker compose down && docker compose up -d
步骤3:备份所有内容
/home/deploy/n8n/backup-n8n.sh
这是你的回滚点。如果2.x破坏了你的设置,可以恢复此备份并留在1.x。
步骤4:更新到2.x
编辑docker-compose.yml:
services:
n8n:
image: n8nio/n8n:stable # Or a specific 2.x version like 2.12.3
docker compose pull
docker compose down
docker compose up -d
步骤5:验证迁移
docker compose logs --tail 100 n8n
查找已完成的迁移且无错误。
测试迁移报告标记的工作流。打开凭据确认解密正常。端到端运行几个工作流。
如果有问题,使用上一节的流程用1.x备份和1.x镜像标签进行回滚。
故障排除
pg_dump: error: connection to server failed
PostgreSQL容器未运行。用docker compose up -d postgres启动它,等它就绪后再运行备份。
恢复后凭据显示为空
你的N8N_ENCRYPTION_KEY与保存凭据时使用的不匹配。检查.env文件,与密码管理器中的密钥对比。
docker exec失败显示"no such container"
容器名称取决于项目目录名和compose文件。运行docker ps查找实际容器名称。调整备份脚本中的DB_CONTAINER变量。
备份脚本运行但healthcheck从未ping
检查脚本中是否设置了HEALTHCHECK_URL。手动测试URL:curl -fsS "your-healthcheck-url"。防火墙规则可能阻止了出站HTTPS。
更新后n8n无法启动并显示迁移错误
用docker compose logs n8n检查日志。如果错误提到特定迁移,在n8n社区论坛搜索该错误。必要时回滚。
rclone sync因认证错误失败
运行rclone config reconnect n8n-backup:刷新凭据。验证访问密钥和秘密密钥是否正确。
日志位置:
# n8n application logs
docker compose logs -f n8n
# PostgreSQL logs
docker compose logs -f postgres
# Backup script log
tail -f /home/deploy/n8n-backups/backup.log
# Cron execution log
grep CRON /var/log/syslog | tail -20
版权所有 2026 Virtua.Cloud。保留所有权利。 本内容为 Virtua.Cloud 团队原创作品。 未经书面许可,禁止复制、转载或再分发。