在VPS上使用Docker Compose安装n8n
在VPS上使用Docker Compose和PostgreSQL部署n8n 2.x。从一开始就使用生产级配置:固定版本、加密密钥备份、健康检查和资源限制。
本指南在VPS上使用Docker Compose和PostgreSQL安装n8n 2.12.3。大约15分钟内你就能拥有一个可用的n8n实例。配置从一开始就使用生产级默认值:固定镜像版本、加密凭据、端口仅绑定到localhost、两个容器都有健康检查以及Docker资源限制。本文不涉及反向代理和SSL,相关内容见。
安装n8n之前需要什么?
你需要一台运行Debian 12或Ubuntu 24.04的VPS,至少4 GB内存。Docker和Docker Compose(docker compose插件,不是旧的docker-compose二进制文件)必须已安装。如果还没有安装Docker,请先参照Docker Compose多服务VPS部署。
验证Docker Compose可用:
docker compose version
预期输出:
Docker Compose version v2.x.x
如果此命令报错docker: 'compose' is not a docker command,说明你安装的是旧版独立二进制文件。请从Docker官方仓库安装Docker Compose插件。
你还需要一个拥有sudo权限的非root用户。本指南中所有命令都以该用户身份运行,不使用root。
为什么n8n要用PostgreSQL而不是SQLite?
PostgreSQL处理并发连接,支持WAL崩溃恢复,并且在n8n运行时可以使用pg_dump进行热备份。SQLite在每次写入时锁定整个数据库文件。在并发webhook执行下,这会导致超时和数据损坏。在n8n运行时无法安全备份SQLite数据库,否则会产生损坏的副本。除了本地测试之外,PostgreSQL是正确的选择。
| 特性 | SQLite | PostgreSQL |
|---|---|---|
| 并发写入 | 单写锁 | 完整MVCC |
| 热备份 | 运行时不安全 | 随时pg_dump |
| 崩溃恢复 | 手动journal重放 | 自动WAL重放 |
| 扩展性 | 单进程 | 连接池 |
| n8n环境变量 | DB_TYPE=sqlite |
DB_TYPE=postgresdb |
如何在VPS上使用Docker Compose安装n8n?
创建项目目录,编写包含密钥的.env文件,编写docker-compose.yml,启动stack并验证一切正常。每个步骤都包含验证检查。
创建项目目录
mkdir -p ~/n8n && cd ~/n8n
创建.env文件
所有密钥都放在.env文件中。不要在docker-compose.yml中硬编码密码或密钥。
生成强数据库密码和n8n加密密钥:
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
添加其余变量:
cat >> .env << 'EOF'
# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_DB=n8n
# n8n
N8N_VERSION=2.12.3
N8N_HOST=localhost
N8N_PORT=5678
N8N_PROTOCOL=http
N8N_DIAGNOSTICS_ENABLED=false
GENERIC_TIMEZONE=UTC
EOF
锁定文件权限。只有你的用户可以读取此文件:
chmod 600 .env
验证:
ls -la .env
你应该看到-rw-------。服务器上的其他用户无法读取你的数据库密码或加密密钥。
如何生成和备份n8n加密密钥?
上面使用openssl rand -hex 32生成了N8N_ENCRYPTION_KEY。这会产生一个32字节(64个十六进制字符)的随机密钥。n8n使用此密钥加密你存储的所有凭据:API密钥、OAuth令牌、工作流中的数据库密码。如果丢失此密钥,所有存储的凭据将永久不可读。没有恢复机制。
立即备份加密密钥。将其复制到密码管理器或离线保险库:
grep N8N_ENCRYPTION_KEY .env
将输出保存在此服务器之外的安全位置。在n8n中添加第一个凭据之前完成此操作。
环境变量参考
| 变量 | 用途 | 示例值 |
|---|---|---|
POSTGRES_USER |
PostgreSQL超级用户名 | n8n |
POSTGRES_PASSWORD |
PostgreSQL超级用户密码 | (已生成,32+字符) |
POSTGRES_DB |
数据库名 | n8n |
N8N_VERSION |
固定的n8n镜像标签 | 2.12.3 |
N8N_ENCRYPTION_KEY |
加密存储的凭据 | (已生成,64个十六进制字符) |
N8N_HOST |
n8n界面的主机 | localhost |
N8N_PORT |
n8n界面的端口 | 5678 |
N8N_PROTOCOL |
HTTP或HTTPS | http |
N8N_DIAGNOSTICS_ENABLED |
向n8n发送遥测数据 | false |
GENERIC_TIMEZONE |
cron触发器的时区 | UTC |
编写docker-compose.yml
cat > docker-compose.yml << 'COMPOSE'
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
n8n:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: unless-stopped
environment:
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}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: ${N8N_HOST}
N8N_PORT: ${N8N_PORT}
N8N_PROTOCOL: ${N8N_PROTOCOL}
N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
deploy:
resources:
limits:
memory: 2G
cpus: "2.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
postgres_data:
n8n_data:
COMPOSE
这个文件中有几点值得注意:
没有version:键。 Docker Compose V2会忽略它。所有竞品教程仍然包含version: '3.7'或version: '3.8'。Compose规范已弃用此字段。在当前Docker版本中包含它会产生警告。
端口绑定到127.0.0.1。 "127.0.0.1:5678:5678"这一行将n8n限制在localhost。这是安全要求,不是偏好设置。Docker端口映射完全绕过iptables和UFW规则。如果写成5678:5678而没有127.0.0.1前缀,即使防火墙阻止了5678端口,n8n也可以从互联网访问。配置反向代理后,同一台机器上的反向代理会将流量转发到localhost:5678。
security_opt: no-new-privileges:true 阻止容器内的进程通过setuid或setgid二进制文件获取额外权限。这是防御容器逃逸攻击的纵深防御措施。
日志轮转。 logging块将每个容器的JSON日志限制为3个文件,每个10 MB(每个服务最多30 MB)。没有这个设置,Docker日志会不断增长直到磁盘空间耗尽。在存储有限的VPS上,这很重要。
两个服务都有健康检查。 PostgreSQL使用pg_isready。n8n使用其/healthz端点。depends_on条件确保n8n仅在PostgreSQL通过健康检查后才启动。
资源限制。 PostgreSQL获得512 MB内存和1个CPU。n8n获得2 GB内存和2个CPU。这些数值在4-8 GB的VPS上运行良好。根据你的服务器大小和工作流复杂度进行调整。
命名卷。 postgres_data和n8n_data都是Docker管理的命名卷。Docker自动处理卷内的所有权和权限。无需创建主机目录或手动修复权限。
restart: unless-stopped 而不是restart: always。两者都会在崩溃后重启,但unless-stopped尊重手动docker compose stop命令。使用restart: always时,手动停止的容器会在Docker守护进程重启时(例如系统更新后)自动重启。
启动stack
cd ~/n8n
docker compose up -d
观察首次启动时的日志:
docker compose logs -f
等待n8n输出包含n8n ready on的行。按Ctrl+C退出日志查看器。
验证安装
检查两个容器是否都在运行且健康:
docker compose ps
预期输出:
NAME IMAGE ... STATUS PORTS
n8n-n8n-1 docker.n8n.io/n8nio/n8n:2.12.3 ... Up X minutes (healthy) 127.0.0.1:5678->5678/tcp
n8n-postgres-1 postgres:16-alpine ... Up X minutes (healthy)
注意看:两个容器的STATUS列都显示(healthy)。这确认健康检查正在通过。如果看到(health: starting),等待30秒后再次检查。
从服务器测试n8n API:
curl -s http://localhost:5678/healthz
预期输出:
{"status":"ok"}
验证端口仅在localhost上监听,而不是所有接口:
ss -tlnp | grep 5678
你应该在输出中看到127.0.0.1:5678。如果看到0.0.0.0:5678,说明端口绑定有误。停止stack,修正docker-compose.yml中的ports行,然后重新启动。
如何创建n8n所有者账户?
从本地机器打开SSH隧道,通过浏览器访问n8n:
ssh -L 5678:127.0.0.1:5678 your-user@your-server-ip
在浏览器中打开http://localhost:5678。n8n在首次访问时显示设置界面。使用强密码创建你的所有者账户。此账户拥有n8n的完整管理员权限。
创建账户后,关闭SSH隧道。不要让5678端口的隧道保持超过必要的时间。设置带SSL的反向代理用于日常访问。见。
如何验证n8n正在正确运行?
创建账户并关闭SSH隧道后,从服务器运行最后一组检查。
检查容器健康状态:
docker compose ps --format "table {{.Name}}\t{{.Status}}"
两个服务都应显示(healthy)。
检查n8n日志中的错误:
docker compose logs n8n --tail 20
查找ERROR行。正常启动会显示数据库迁移消息,然后是n8n ready on。
检查PostgreSQL日志:
docker compose logs postgres --tail 10
你应该看到database system is ready to accept connections。
检查Docker卷的磁盘使用情况:
docker system df -v | grep -E "n8n|postgres"
这会告诉你n8n和PostgreSQL使用了多少空间。在存储有限的VPS实例上定期检查。
出了问题?
容器立即退出。 用docker compose logs n8n检查日志。常见原因:缺少.env文件、PostgreSQL密码错误,或者加密密钥包含破坏shell展开的特殊字符。必要时重新生成.env。
权限错误。 n8n容器以UID 1000运行。如果从命名卷切换到bind mount,确保主机目录属于UID 1000:sudo chown -R 1000:1000 ./n8n_data。
健康检查失败。 n8n首次启动时需要20-30秒来执行数据库迁移。如果健康检查失败,检查docker compose logs n8n中的迁移错误。start_period: 30s设置在健康检查开始前给n8n留出时间。
无法连接到n8n。 端口绑定到localhost。没有SSH隧道或反向代理,你无法从其他机器访问。这是有意设计的。
忘记了加密密钥。 如果丢失了N8N_ENCRYPTION_KEY且n8n已存储凭据,这些凭据就永远丢失了。没有恢复方法。这就是备份步骤存在的原因。
安装n8n后应该做什么?
此安装给你一个仅从服务器本身可访问的n8n实例。用于生产环境,还需要三样东西:
-
带SSL的反向代理。 在n8n前面配置Nginx或Caddy并部署TLS证书。这让你能通过域名以HTTPS方式访问。见。
-
备份。 安排PostgreSQL数据库和n8n加密密钥的自动备份。见。
-
更新。 要更新n8n,将
.env中的N8N_VERSION改为新版本,然后运行docker compose up -d。Docker会拉取新镜像并重建容器。更新前务必阅读n8n发布说明。
关于VPS上工作流自动化选项的上级指南,见。
关于Docker Compose基础知识和多服务管理,见Docker Compose多服务VPS部署。
Copyright 2026 Virtua.Cloud. All rights reserved.