第 3 期:环境部署 — Docker Compose 本地部署与安全配置

⏱ 预计阅读 16 分钟 更新于 2026/4/10

部署架构总览

在开始动手之前,先理解我们要搭建的系统全貌:

graph TB
    subgraph "Docker Host (你的服务器/笔记本)"
        subgraph "Docker Compose 集群"
            N8N[🤖 n8n 容器
端口 5678] PG[🐘 PostgreSQL 容器
端口 5432] N8N -->|"存储工作流/凭证/执行日志"| PG end VOL_N8N[📁 n8n_data 卷
/home/node/.n8n] VOL_PG[📁 postgres_data 卷
/var/lib/postgresql/data] N8N -.->|"持久化文件/社区节点"| VOL_N8N PG -.->|"持久化数据库"| VOL_PG end USER[👤 用户浏览器] -->|"https://localhost:5678"| N8N WEBHOOK[🌐 外部 Webhook] -->|"POST /webhook/xxx"| N8N style N8N fill:#ff6d5b,stroke:#e55a4e,color:#fff style PG fill:#336791,stroke:#2d5a7b,color:#fff

第一步:创建项目目录

# 创建一个专门的 n8n 工作目录
# Create a dedicated n8n working directory
mkdir -p ~/n8n-docker && cd ~/n8n-docker

# 创建环境变量文件(重要:绝不要把密钥写在 docker-compose.yml 中!)
# Create environment file (IMPORTANT: NEVER put secrets in docker-compose.yml!)
touch .env

第二步:编写 .env 环境变量文件

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# n8n Docker 环境变量配置文件 (.env)
# 所有敏感配置都集中在此文件中,docker-compose.yml 通过 ${VAR} 引用
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# ── 核心加密 ──────────────────────────────────────────
# 这是 n8n 用于加密所有 API 凭证的主密钥(AES-256)
# 一旦设定后不可更改!否则所有已保存的凭证将无法解密
# 建议使用 openssl 生成: openssl rand -hex 32
N8N_ENCRYPTION_KEY=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

# ── 数据库配置 ────────────────────────────────────────
# 生产环境强烈建议使用 PostgreSQL 而非默认的 SQLite
# PostgreSQL 支持并发写入和大量执行记录
POSTGRES_USER=n8n                    # 数据库用户名
POSTGRES_PASSWORD=your_strong_pw_here  # 数据库密码(请替换!)
POSTGRES_DB=n8n_production           # 数据库名称

# 告诉 n8n 使用 PostgreSQL
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres          # Docker Compose 内部网络的服务名
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
DB_POSTGRESDB_USER=${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}

# ── 访问控制 ────────────────────────────────────────
# 设置 n8n 后台的登录用户名和密码
N8N_BASIC_AUTH_ACTIVE=true           # 启用基础身份验证
N8N_BASIC_AUTH_USER=admin            # 管理员用户名
N8N_BASIC_AUTH_PASSWORD=change_me!   # 管理员密码(请替换!)

# ── 网络配置 ────────────────────────────────────────
# Webhook 的公开访问地址(用于 Telegram Bot / Stripe Webhook 等场景)
WEBHOOK_URL=https://n8n.yourdomain.com/
# n8n 编辑器的协议
N8N_PROTOCOL=http
N8N_PORT=5678

# ── 时区 ─────────────────────────────────────────────
# 确保 Schedule Trigger 按你的本地时间执行
GENERIC_TIMEZONE=Asia/Shanghai
TZ=Asia/Shanghai

# ── 执行安全 ────────────────────────────────────────
# 启用 Task Runner(n8n 2.0 新增),在隔离沙箱中运行 Code 节点
# 防止恶意代码访问宿主文件系统
N8N_RUNNERS_ENABLED=true

第三步:编写 docker-compose.yml

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# n8n Docker Compose 配置文件
# 版本: 适配 Docker Compose V2 (无需 version 字段)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

services:
  # ── n8n 主服务 ─────────────────────────────────────
  n8n:
    image: n8nio/n8n:latest          # 官方镜像,始终拉取最新稳定版
    container_name: n8n
    restart: unless-stopped          # 崩溃后自动重启,手动停止则不重启
    ports:
      - "5678:5678"                  # 映射端口:宿主机 5678 → 容器 5678
    environment:
      # 从 .env 文件中读取所有环境变量
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}     # 凭证加密密钥
      - DB_TYPE=${DB_TYPE}                            # 数据库类型
      - DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}      # PostgreSQL 地址
      - DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
      - DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
      - DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
      - DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
      - N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE}
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${TZ}
      - N8N_RUNNERS_ENABLED=${N8N_RUNNERS_ENABLED}    # Code 节点沙箱隔离
    volumes:
      - n8n_data:/home/node/.n8n     # 持久化: 社区节点、文件上传等
    depends_on:
      postgres:
        condition: service_healthy   # 等待 PostgreSQL 健康检查通过后再启动
    networks:
      - n8n_network

  # ── PostgreSQL 数据库 ──────────────────────────────
  postgres:
    image: postgres:16-alpine        # Alpine 精简版,体积更小
    container_name: n8n_postgres
    restart: unless-stopped
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql/data  # 持久化: 数据库文件
    healthcheck:
      # 每 5 秒检查一次数据库是否就绪
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - n8n_network

# ── 持久化卷 ──────────────────────────────────────────
volumes:
  n8n_data:        # n8n 工作数据
  postgres_data:   # PostgreSQL 数据

# ── 内部网络 ──────────────────────────────────────────
networks:
  n8n_network:
    driver: bridge   # 容器间通过服务名互相访问 (如: postgres:5432)

第四步:启动与验证

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 启动 n8n 集群 (后台运行)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker compose up -d
# 输出: [+] Running 2/2
#        ✔ Container n8n_postgres Started
#        ✔ Container n8n            Started

# 查看容器运行状态
docker compose ps
# 预期输出:
# NAME             IMAGE                 STATUS               PORTS
# n8n              n8nio/n8n:latest      Up 30 seconds        0.0.0.0:5678->5678/tcp
# n8n_postgres     postgres:16-alpine    Up 31 seconds (healthy)

# 查看 n8n 启动日志(确认无报错)
docker compose logs n8n --tail 20
# 关键看这行: "n8n ready on 0.0.0.0, port 5678"

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 验证服务是否正常响应
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
curl -s http://localhost:5678/healthz
# 预期输出: {"status":"ok"}

部署启动流程的时序图

sequenceDiagram
    participant Dev as 👤 开发者
    participant DC as 🐳 Docker Compose
    participant PG as 🐘 PostgreSQL
    participant N8N as 🤖 n8n

    Dev->>DC: docker compose up -d
    DC->>PG: 启动 PostgreSQL 容器
    
    loop 健康检查 (每 5 秒)
        DC->>PG: pg_isready?
        PG-->>DC: Not ready...
    end
    PG-->>DC: ✅ Ready!
    
    DC->>N8N: 启动 n8n 容器
    N8N->>PG: 连接数据库
    N8N->>N8N: 运行数据库迁移
    N8N->>N8N: 加载加密密钥
    N8N-->>Dev: 🟢 n8n ready on port 5678
    
    Dev->>N8N: 打开浏览器 → localhost:5678
    N8N-->>Dev: 显示登录/注册界面

常见问题排查

问题现象 可能原因 解决方案
容器反复重启 N8N_ENCRYPTION_KEY 未设置 检查 .env 文件是否被正确加载
无法登录 密码含特殊字符被 shell 转义 .env 中用引号包裹密码值
Webhook URL 不生效 WEBHOOK_URL 配置错误 确保包含协议前缀 https://
数据库连接失败 PostgreSQL 尚未就绪 depends_on + healthcheck 已处理
中文时间不对 时区未配置 确认 TZ=Asia/Shanghai 已设置

升级与备份

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 安全升级 n8n 到最新版本
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# 第一步:备份数据库(养成好习惯!)
docker exec n8n_postgres pg_dump -U n8n n8n_production > backup_$(date +%Y%m%d).sql

# 第二步:拉取最新镜像
docker compose pull

# 第三步:无感重启(数据卷不会丢失)
docker compose up -d

# 第四步:验证版本
docker exec n8n n8n --version
# 输出: 1.x.x (最新版本号)

下一步

环境已经就绪!在 Ep 04 中,我们将深入 n8n 最核心也是最容易被误解的概念——JSON Data Items 的隐式循环机制,理解为什么你几乎永远不需要手写 for 循环。