在VPS上使用Docker Compose安装n8n

2 分钟阅读·Matthieu|

在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 阻止容器内的进程通过setuidsetgid二进制文件获取额外权限。这是防御容器逃逸攻击的纵深防御措施。

日志轮转。 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_datan8n_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实例。用于生产环境,还需要三样东西:

  1. 带SSL的反向代理。 在n8n前面配置Nginx或Caddy并部署TLS证书。这让你能通过域名以HTTPS方式访问。见。

  2. 备份。 安排PostgreSQL数据库和n8n加密密钥的自动备份。见。

  3. 更新。 要更新n8n,将.env中的N8N_VERSION改为新版本,然后运行docker compose up -d。Docker会拉取新镜像并重建容器。更新前务必阅读n8n发布说明。

关于VPS上工作流自动化选项的上级指南,见。

关于Docker Compose基础知识和多服务管理,见Docker Compose多服务VPS部署


Copyright 2026 Virtua.Cloud. All rights reserved.

准备好亲自尝试了吗?

几秒内部署您自己的服务器。支持 Linux、Windows 或 FreeBSD。

查看 VPS 方案