第 3 期:环境部署 — Docker Compose 本地部署与安全配置
部署架构总览
在开始动手之前,先理解我们要搭建的系统全貌:
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 循环。