1. 为什么选择 Docker Compose 来部署 n8n?

如果你正在寻找一个强大、灵活且能连接一切的工作流自动化工具,n8n 绝对是个宝藏。但当你兴奋地准备把它用起来时,第一个拦路虎往往就是部署。官方提供了好几种方式,比如直接用 npm 安装、用 Docker 跑单个容器,或者直接用他们的云服务。那为什么我,以及很多社区里的老手,都更推荐用 Docker Compose 来部署,尤其是想用在生产环境的时候呢?

简单来说,Docker Compose 把“全家桶”打包管理了。n8n 本身是一个应用,但在生产环境里,它很少是孤零零的。你通常需要给它配上一个反向代理(比如 Traefik 或 Nginx)来处理 HTTPS 访问、自动申请 SSL 证书,还需要考虑数据持久化、日志收集、依赖服务(比如数据库)等等。如果一个个手动去启动和配置 Docker 容器,命令又长又容易出错,管理起来更是头疼。Docker Compose 用一个 compose.yaml 文件就把所有服务(Service)的定义、网络、存储卷(Volume)关系都描述清楚了。你只需要一个 docker compose up -d 命令,所有服务就按正确的顺序启动并连接好了,干净利落。

我自己在早期也试过只跑一个 n8n 容器,然后手动去配置 Nginx。结果每次更新镜像或者调整配置,都要小心翼翼地操作好几个地方,生怕把服务搞挂了。换成 Docker Compose 之后,整个部署变得像版本控制代码一样清晰。所有的配置都白纸黑字写在文件里,迁移到新服务器?直接复制整个文件夹过去就行。回滚到上一个版本?改一下镜像标签重新启动就好。这种可重复、可维护的部署方式,才是生产环境所需要的“稳”。接下来,我就带你从零开始,一步步搭建起这套稳健的 n8n 生产环境。

2. 基础环境准备:安装 Docker 与 Docker Compose

万事开头难,但把基础打牢了,后面就一马平川。我们首先需要在你的 Linux 服务器上安装 Docker 和 Docker Compose。这里我以最常用的 Ubuntu 22.04 LTS 为例,其他发行版(如 CentOS、Debian)的命令逻辑类似,主要是包管理器的区别。

2.1 彻底清理旧版本(避免冲突)

这一步很多人会忽略,但特别重要。如果你的系统之前装过老旧的 Docker 版本(比如 docker.io)或者其他容器工具(如 Podman),可能会和新版本产生冲突。所以咱们先来个大扫除。

打开你的终端,依次执行下面的命令。别怕,我们一条条来,我还会解释每条命令在干嘛:

# 1. 卸载所有可能冲突的旧包
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
    sudo apt-get remove $pkg -y
done

这条命令用一个循环,把列表里这些可能存在的旧包都移除掉。-y 参数是自动回答“yes”,省得我们手动确认。

# 2. 清理残留的配置和数据(可选但推荐)
sudo apt-get autoremove -y
sudo apt-get autoclean

autoremove 会删除那些被卸载软件所依赖、但现在不再需要的包。autoclean 则清理本地已下载的旧软件包缓存。做完这些,咱们的“手术台”就干净了。

2.2 安装 Docker 官方最新版本

现在,我们来安装 Docker 官方提供的最新稳定版。Docker 官方维护了一个软件源,比系统自带的仓库更新、更可靠。

# 1. 更新系统包列表并安装一些基础工具
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg -y

ca-certificates 是 HTTPS 连接必需的证书,curl 是用来下载文件的工具,gnupg 用来处理加密密钥。

# 2. 创建密钥环目录,并添加 Docker 的官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

GPG 密钥就像 Docker 官方的“数字签名”,添加它之后,系统才能验证从 Docker 仓库下载的软件包是真实且未被篡改的。-fsSL 参数让 curl 在失败时静默、跟随重定向,并以安全(SSL)模式运行。

# 3. 将 Docker 的 APT 软件源添加到系统列表
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

这条命令看起来复杂,其实是在做一件事:根据你系统当前的架构(比如 x86_64)和版本代号(比如 jammy),生成正确的软件源地址,并写入到 /etc/apt/sources.list.d/docker.list 这个文件里。tee 命令同时完成写入文件和显示内容,我们这里用 > /dev/null 把屏幕输出丢弃,让界面更干净。

# 4. 再次更新包列表,并安装 Docker 引擎及相关组件
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

这里安装了 Docker 的核心引擎(docker-ce)、命令行工具、容器运行时(containerd),以及两个非常实用的插件:docker-buildx-plugin(用于构建多平台镜像)和 docker-compose-plugin。注意,从 Docker 官方源安装的 docker-compose-plugin 就是 Docker Compose V2,它比旧的 V1 版本功能更强、速度更快,而且命令是 docker compose(中间没有横线),我们之后就用这个。

2.3 验证安装与配置非 Root 用户执行

安装完成后,马上验证一下,这是好习惯。

# 检查 Docker 和 Docker Compose 版本
docker --version
docker compose version

如果看到输出了版本号,比如 Docker version 24.0.7 和 Docker Compose version v2.23.0,那就恭喜你,安装成功了!

不过,默认情况下,运行 docker 命令需要 sudo 权限,这很不方便也不安全。我们应该把当前用户加入到 docker 用户组。

# 将当前用户加入 docker 组
sudo usermod -aG docker $USER

执行完这条命令后,最关键的一步来了:你需要完全退出当前终端,然后重新登录,或者新开一个终端窗口。只有这样,新的组权限才会生效。之后,你再运行 docker ps 之类的命令,就不需要加 sudo 了,会方便很多。

3. 部署前的关键规划:域名、目录与配置文件

环境准备好了,先别急着敲部署命令。好的规划是成功的一半,尤其是在生产环境。这一步我们要想清楚三件事:用什么域名访问、文件放在哪里、关键配置怎么定。

3.1 域名与 DNS 解析

在生产环境,我们肯定不希望用 http://服务器IP:5678 这种又难记又不安全的方式访问 n8n。我们需要一个域名,并且配置 HTTPS。假设你有一个自己的域名 example.com,我强烈建议为 n8n 创建一个子域名,比如 n8n.example.com。这样做有几个好处:一是隔离性好,不会影响主域名的其他服务;二是证书管理方便;三是看起来更专业。

你需要去你的域名注册商或 DNS 服务商那里,为这个子域名添加一条 A 记录,将其指向你的服务器的公网 IP 地址。DNS 记录生效可能需要几分钟到几小时,我们可以先进行后续步骤。

3.2 创建项目目录结构

我习惯为每个 Docker Compose 项目创建一个独立的目录,这样所有相关文件都在一起,管理起来一目了然。我们在用户的家目录或者你喜欢的任何位置创建一个新目录。

mkdir ~/n8n-production
cd ~/n8n-production

这个 n8n-production 目录就是我们的“项目根目录”,接下来所有操作都在这里进行。

3.3 编写核心配置文件:.env

Docker Compose 支持使用 .env 文件来定义环境变量,这样我们可以把配置(比如域名、邮箱)从 compose.yaml 里分离出来,更灵活也更安全(避免把敏感信息硬编码在 YAML 文件里)。

在项目根目录下创建一个名为 .env 的文件:

nano .env

然后填入以下内容,并根据你的实际情况修改:

# 你的主域名
DOMAIN_NAME=example.com
# n8n 要使用的子域名前缀
SUBDOMAIN=n8n
# n8n 服务器使用的时区,建议设置为 Asia/Shanghai
GENERIC_TIMEZONE=Asia/Shanghai
# 用于申请 Let‘s Encrypt SSL 证书的邮箱,证书过期前会收到提醒
SSL_EMAIL=your-email@example.com
# (可选但推荐)设置一个强密码,替换下面的 ‘your_secure_password_here’
N8N_BASIC_AUTH_PASSWORD=your_secure_password_here

重要提示:N8N_BASIC_AUTH_PASSWORD 是你登录 n8n 后台的密码,务必设置一个复杂且唯一的密码,不要使用示例中的 admin 或简单的字符串。稍后我们会在 Docker Compose 文件中引用这个变量。

3.4 创建数据持久化目录

n8n 在工作过程中会产生很多数据,比如你创建的工作流、执行的日志、凭证信息等。我们必须把这些数据保存在容器之外,也就是“持久化”,否则容器一删除,所有数据就没了。

我们创建一个目录,用来映射 n8n 容器内存放用户上传文件的位置。

mkdir ./local-files

这个 local-files 目录会在后面的 Docker Compose 配置中,挂载到 n8n 容器内的 /files 路径。这样,你在工作流中读写文件,数据都会安全地保存在宿主机上。

4. 编写 Docker Compose 配置:让一切自动运转

这是最核心的一步,我们将编写 compose.yaml 文件,定义两个服务:traefik 作为反向代理和 SSL 证书管理器,n8n 作为我们的自动化平台。

在项目根目录下创建 compose.yaml 文件:

nano compose.yaml

将以下配置内容粘贴进去。别担心,我会逐段解释每个部分的作用。

version: '3.8'  # 指定 Compose 文件格式版本

services:
  # 服务一:Traefik - 智能反向代理
  traefik:
    image: traefik:latest  # 使用官方最新镜像
    container_name: traefik  # 为容器指定一个名字,方便管理
    restart: unless-stopped  # 除非手动停止,否则总是重启(应对意外退出)
    command:
      # 启用 Web API 和 Dashboard(生产环境建议关闭或加强认证)
      - "--api.insecure=true"
      # 启用 Docker 提供者,让 Traefik 能自动发现其他容器
      - "--providers.docker=true"
      # 默认不暴露所有容器,只有打了特定标签的容器才会被代理
      - "--providers.docker.exposedbydefault=false"
      # 定义两个入口点:web (HTTP 80端口) 和 websecure (HTTPS 443端口)
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      # 自动将 HTTP 80 端口的流量重定向到 HTTPS 443 端口
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      # 配置 Let‘s Encrypt 证书解析器,使用 TLS-ALPN-01 挑战方式
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      # 证书申请邮箱,从 .env 文件读取
      - "--certificatesresolvers.letsencrypt.acme.email=${SSL_EMAIL}"
      # 证书存储位置,保存在 Docker 卷中
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      # 将宿主机的 80 和 443 端口映射到容器内
      - "80:80"
      - "443:443"
    volumes:
      # 挂载 Docker 卷,用于持久化存储 SSL 证书
      - traefik_data:/letsencrypt
      # 挂载 Docker 守护进程套接字,这是 Traefik 能监听其他容器的关键
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      # 让 Traefik 接入一个自定义网络,以便与 n8n 通信
      - n8n-network

  # 服务二:n8n - 工作流自动化平台
  n8n:
    image: n8nio/n8n:latest  # 使用 n8n 官方最新镜像
    container_name: n8n
    restart: unless-stopped
    environment:
      # 设置时区,保证定时任务等时间相关功能正确
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      # 启用基础身份验证(必须开启,否则后台直接暴露)
      - N8N_BASIC_AUTH_ACTIVE=true
      # 设置登录用户名
      - N8N_BASIC_AUTH_USER=admin
      # 设置登录密码,从 .env 文件读取,更安全
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      # 设置 Webhook URL 的基础地址,用于生成正确的回调地址
      - WEBHOOK_TUNNEL_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      # (可选)设置加密密钥,用于加密保存的凭证。生产环境强烈建议设置!
      # - N8N_ENCRYPTION_KEY=your_super_strong_secret_key_here
    labels:
      # 告诉 Traefik 这个容器需要被代理
      - "traefik.enable=true"
      # 定义路由规则:当访问的主机名是 n8n.example.com 时,流量转发到此容器
      - "traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)"
      # 指定使用 websecure (HTTPS) 入口点
      - "traefik.http.routers.n8n.entrypoints=websecure"
      # 为此路由启用 TLS,并使用我们上面配置的 letsencrypt 解析器自动申请证书
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
    volumes:
      # 挂载 Docker 卷,持久化 n8n 的核心数据(工作流、设置、凭证等)
      - n8n_data:/home/node/.n8n
      # 挂载本地目录,用于文件存储
      - ./local-files:/files
    depends_on:
      # 声明依赖关系:先启动 traefik,再启动 n8n
      - traefik
    networks:
      # 接入同一个自定义网络
      - n8n-network

# 定义 Docker 卷,用于持久化数据
volumes:
  n8n_data:  # n8n 数据卷
  traefik_data:  # Traefik 证书数据卷

# 定义自定义网络,让两个服务在隔离的网络内通信
networks:
  n8n-network:
    driver: bridge

这个配置看起来有点长,但结构非常清晰。它实现了:自动申请并续期 HTTPS 证书、HTTP 到 HTTPS 强制跳转、基于子域名的路由、以及核心数据的持久化。你保存这个文件后,90%的运维工作就已经被自动化了。

5. 启动服务与验证:见证奇迹的时刻

配置写完,终于到了最激动人心的启动环节。整个过程非常简单,这恰恰是 Docker Compose 的魅力所在。

5.1 一键启动所有服务

确保你的终端位于项目根目录(~/n8n-production),然后执行:

docker compose up -d

这个 -d 参数代表“分离模式”,意思是在后台运行。命令执行后,Docker 会依次做以下几件事:

  1. 检查并拉取所需的镜像(traefik:latest 和 n8nio/n8n:latest)。
  2. 根据 compose.yaml 的定义,创建名为 n8n-production_n8n-network 的自定义网络(名字加了项目前缀)。
  3. 创建 n8n-production_n8n_data 和 n8n-production_traefik_data 两个持久化卷。
  4. 按依赖顺序启动容器:先启动 traefik,再启动 n8n。

你可以用以下命令查看容器的运行状态和日志:

# 查看所有容器的运行状态
docker compose ps
# 查看 n8n 容器的实时日志(按 Ctrl+C 退出)
docker compose logs -f n8n
# 查看 traefik 容器的实时日志
docker compose logs -f traefik

如果看到状态是 Up,并且日志中没有持续报错,就说明启动基本成功了。

5.2 验证访问与初始登录

现在,打开你的浏览器,访问 https://n8n.example.com(请将域名替换成你在 .env 文件中设置的)。如果一切顺利,你应该会看到 n8n 的登录界面,并且浏览器地址栏显示有一个安全的锁标志(表示 HTTPS 生效)。

第一次登录,使用我们在环境变量中设置的用户名(默认是 admin)和密码(你设置的强密码)。登录成功后,你就进入了 n8n 的编辑界面!这意味着你的私有化、支持 HTTPS 的自动化平台已经正式上线。

5.3 管理服务常用命令

掌握几个简单的命令,你就能轻松管理整个应用栈:

# 停止所有服务(容器停止但未删除)
docker compose stop
# 启动已停止的服务
docker compose start
# 重启所有服务(常用干更新配置后)
docker compose restart
# 停止并删除所有容器、网络(但保留卷和数据)
docker compose down
# 停止并删除所有容器、网络、以及我们在 compose.yaml 中定义的卷(数据会被清除!慎用)
docker compose down -v
# 拉取最新的镜像并重新启动服务(用于升级)
docker compose pull && docker compose up -d

6. 生产环境进阶配置与优化建议

基础部署完成了,但要让 n8n 在生产环境里真正“扛打”,我们还需要做一些加固和优化。这些步骤能显著提升安全性、可靠性和性能。

6.1 安全性加固:这是重中之重

1. 更改默认用户名和强密码:我们已经在 .env 文件中用变量设置了密码,这比写在 YAML 里好。但你也可以考虑将用户名也变量化,或者登录后第一时间在 n8n 设置里更改。绝对不要使用默认的 admin/admin。

2. 设置加密密钥(N8N_ENCRYPTION_KEY):这是很多新手会忽略的致命点。n8n 会将你配置的各种 API 令牌、密码等凭证加密后存储。如果未设置加密密钥,它会使用一个默认的、公开的密钥,这意味着任何人拿到你的数据卷都能解密你的凭证!务必在 compose.yaml 的 n8n 环境变量部分取消注释并设置一个强密钥。

environment:
  - N8N_ENCRYPTION_KEY=your_very_long_and_random_encryption_key_here

你可以用 openssl rand -base64 32 命令生成一个足够强的随机字符串。

3. 考虑禁用或保护 Traefik Dashboard:我们配置中 --api.insecure=true 是启用了 Traefik 的 API 和 Dashboard,这方便调试,但暴露在公网有风险。生产环境可以考虑: * 完全禁用:移除该参数。 * 通过 IP 限制访问:配合 Traefik 的中间件(Middleware)功能,只允许特定 IP 访问 Dashboard。 * 设置基础认证:为 Dashboard 单独设置用户名密码。

4. 定期更新镜像:关注 n8nio/n8n 和 traefik 镜像的更新,特别是安全更新。可以定期执行 docker compose pull 来拉取最新镜像,然后 docker compose up -d 重启服务。建议先在测试环境验证。

6.2 数据持久化与备份

我们的配置已经通过 Docker 卷实现了数据持久化。但卷本身也需要备份。

  • 找到你的数据:使用 docker volume inspect n8n-production_n8n_data 命令可以查看卷在宿主机上的实际存储路径(通常在 /var/lib/docker/volumes/...)。
  • 备份方法:最简单的备份就是定时将这个目录打包压缩,然后传输到另一个安全的地方(如对象存储、另一台服务器)。你可以写一个简单的 Shell 脚本,用 cron 定时任务来执行。
  • 考虑使用外部数据库:目前我们使用的是 n8n 内置的 SQLite。对于工作流非常多、执行非常频繁的生产环境,可以考虑配置外部的 PostgreSQL 数据库,性能和管理性都会更好。这需要在 n8n 的环境变量中配置 DB_TYPE、DB_POSTGRESDB_HOST 等参数。

6.3 性能与资源监控

  • 资源限制:可以在 compose.yaml 的 n8n 服务下添加资源限制,防止单个容器占用过多主机资源。
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '0.5'
          memory: 1G
    
  • 日志管理:默认日志会输出到 Docker 守护进程。生产环境建议配置日志驱动(如 json-file 并设置大小和数量限制),或者将日志收集到 ELK、Loki 等集中式日志系统中,方便排查问题。
  • 健康检查:可以为 n8n 服务添加健康检查,让 Docker 能感知应用是否真的健康。
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    

7. 常见问题排查与维护心得

即使按照步骤来,也可能会遇到一些小问题。这里分享几个我踩过的坑和解决办法。

问题一:访问域名显示“Bad Gateway”或“Traefik 404”

  • 可能原因:DNS 解析未生效,或者 Traefik 路由规则配置错误。
  • 排查:
    1. 在服务器上执行 ping n8n.example.com,看是否解析到正确的服务器 IP。
    2. 检查 docker compose logs traefik 日志,看 Traefik 是否成功发现了 n8n 容器并加载了路由规则。你应该能看到类似 \"level=info msg=\"Configuration loaded from file:\\\"/etc/traefik/traefik.yml\\\"\" 和路由创建成功的日志。
    3. 确认 compose.yaml 中 traefik.http.routers.n8n.rule 的 Host 值是否与你的访问域名完全一致。

问题二:HTTPS 证书申请失败

  • 可能原因:Let‘s Encrypt 服务器无法通过 80 或 443 端口访问你的服务器;邮箱地址格式错误;证书保存路径权限问题。
  • 排查:
    1. 确保服务器防火墙(如 ufw)和云服务商的安全组规则已经放行了 80 和 443 端口的入站流量。
    2. 检查 .env 文件中的 SSL_EMAIL 格式是否正确。
    3. 查看 Traefik 日志 docker compose logs traefik,搜索 “acme” 或 “certificate”,通常会有详细的错误信息。

问题三:n8n 启动失败,日志显示权限错误

  • 可能原因:n8n 容器内用户(node)对挂载的卷没有写入权限。
  • 解决:在启动容器前,确保宿主机上创建的 local-files 目录有合适的权限。可以尝试 chmod 777 ./local-files(宽松权限)或更好的是,先启动一次,让容器创建目录结构,再调整所有者 sudo chown -R 1000:1000 ./local-files(因为 n8n 容器内用户 UID 通常是 1000)。

问题四:如何更新 n8n 版本?

  • 步骤:
    1. docker compose pull n8n 拉取最新镜像。
    2. docker compose up -d n8n 重启 n8n 服务。Traefik 会检测到容器重启并重新连接。
    3. 建议在更新前,备份 n8n_data 卷的数据。

关于使用 latest 标签:像我们配置中用的 n8nio/n8n:latest,它会自动拉取最新版本,方便但也存在一定风险(新版本可能有未知问题)。对于追求绝对稳定的生产环境,你可以考虑锁定一个具体的稳定版本号,例如 n8nio/n8n:1.40.0。你可以在 Docker Hub 或 n8n 的 GitHub Release 页面查看版本号。锁定版本后,更新就需要手动修改 compose.yaml 文件中的镜像标签,然后再执行 docker compose up -d。这两种方式没有绝对的对错,取决于你对“新功能”和“稳定性”的权衡。我个人在早期会使用 latest 快速跟进,等系统稳定后,再切换到固定版本号进行更新。

更多推荐