Docker Compose:让 n8n命令变成菜单。
你有没有过这种经历?
第一次部署 n8n 时,照着教程在终端里敲了一大串命令。里面有端口、密码、挂载路径、网络、重启策略。最后它确实跑起来了。
过了两周,电脑重启。你想重新启动 n8n,却发现自己完全忘记当时写过什么。
你开始翻聊天记录、翻浏览器历史、翻收藏夹。终于找到那条命令,又不确定自己有没有改过端口、有没有挂过数据卷、密码是不是同一个。
这不是你记性不好。是因为“把一套应用的所有规则,塞进一条很长的命令”,本来就不适合长期使用。
Docker Compose 就是为了解决这件事。
它让你把 n8n、数据库、端口、数据、网络和启动规则,统一写进一份清单。以后启动不靠回忆,只需要一句命令。
今天我们不追求一口气背完 YAML。我们只做一件事:像给一栋小楼写物业清单那样,把 n8n 和它需要的数据库安排清楚。
先看你最终会得到什么
完成后,你会拥有一个文件夹。里面最重要的是两份文件:
n8n-docker/
├── compose.yaml
└── .env
compose.yaml:公开的物业清单,写着“有哪些房间、怎么连接、数据放哪”。.env:私密的小信封,写着密码和加密密钥。
以后你只要进入这个文件夹,运行:
docker compose up -d
n8n 和 PostgreSQL 数据库会一起启动。
它们各住一间房、各有自己的数据柜、通过内部电话线通信;浏览器只能打开 n8n,不能直接敲数据库的门。
这就是一个对小白来说很重要的“安全边界”:你能用 n8n,但不需要把数据库暴露到外面。
Docker Compose 到底是什么?别把它想复杂
把 Docker Compose 想成一张写得很清楚的物业清单。
假设你要管理两间房:
- 一间叫 n8n,负责自动化。
- 一间叫 PostgreSQL,负责保存数据。
你需要告诉物业:
- 每间房用什么装修模板。
- 谁先开门,谁后开门。
- 哪间房可以对外接待。
- 哪些资料需要放进独立储物柜。
- 两间房如何在楼内打电话。
Docker Compose 就是把这些事写下来。Docker 官方对它的说明是:用一份 YAML 文件来定义、配置和运行多个容器服务。Docker 官方指南
你不需要把 YAML 当成编程语言。它更像“有严格缩进的清单”。只要层级对齐,它就能读懂你的安排。
第 1 步:创建一个专门的文件夹
先在电脑上找一个你方便管理的位置,例如桌面或文稿目录。
新建文件夹:
n8n-docker
接下来所有和 n8n 有关的文件都放在这里。不要今天把 Compose 文件放桌面,明天把密码放下载目录,后天又不知道数据卷从哪来的。
把这个文件夹想成“这栋 n8n 小楼的物业办公室”。
第 2 步:先做 .env,把敏感内容放进小信封
在 n8n-docker 文件夹中,新建一个文件,文件名必须是:
.env
注意两点:前面有英文句点;后面没有 .txt。
把下面内容复制进去:
POSTGRES_USER=n8n
POSTGRES_PASSWORD=请替换成一个你自己设置的强密码
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=请替换成一长串随机字符并安全保存
每一行都不要急着跳过。
| 这一行 | 它在生活中像什么 | 它实际决定什么 |
|---|---|---|
POSTGRES_USER=n8n |
数据库房间的工作人员名字 | n8n 连接 PostgreSQL 时使用的账号。 |
POSTGRES_PASSWORD=... |
数据库房间的门禁密码 | 防止其他人随意进入数据库。 |
POSTGRES_DB=n8n |
这个资料室的名称 | PostgreSQL 启动时创建的数据库名。 |
N8N_ENCRYPTION_KEY=... |
保险柜主钥匙 | n8n 用它保护凭据等敏感信息;以后不要随便换。 |
你现在只要知道一件事:这个文件不应该公开。
如果未来你把 compose.yaml 放到 GitHub,.env 不要一起上传。就像物业清单可以让合作伙伴看,主钥匙不能贴在门口。
第 3 步:复制这一份完整配置,不要先自己拼
在同一个 n8n-docker 文件夹中,新建:
compose.yaml
然后完整复制下面的内容:
services:
postgres:
image: postgres:16
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
networks:
- automation-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
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}
GENERIC_TIMEZONE: Asia/Shanghai
TZ: Asia/Shanghai
volumes:
- n8n_data:/home/node/.n8n
networks:
- automation-net
depends_on:
postgres:
condition: service_healthy
volumes:
n8n_data:
postgres_data:
networks:
automation-net:
driver: bridge
现在先别被这么多行吓到。你不需要一次性理解全部。下面我会像带你参观楼层一样,一段一段讲。
第 4 步:先认识最外层的 services
配置开头是:
services:
它的意思是:这栋楼里有哪些正在营业的房间。
在它下面,你看到了两个名字:
postgres:
n8n:
一个是数据库房间,一个是自动化房间。
这里的名字很重要。以后 n8n 想找数据库,不是去找 localhost,而是直接拨打内部电话簿里的 postgres。
你可以理解成:
n8n 房间 —— 内部电话线 —— postgres 房间
第 5 步:先看 PostgreSQL 房间,它负责“记住”
这一段:
postgres:
image: postgres:16
意思很简单:请 Docker 用 PostgreSQL 16 的标准装修模板,建一间叫 postgres 的数据库房间。
它的密码从哪里来?
看这里:
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
${POSTGRES_PASSWORD} 的意思不是把密码写在这里,而是:请去同一个文件夹的 .env 小信封里,取出这个值。
这样做的好处是,配置结构和真实密码分开。以后改密码,你只改 .env;以后分享配置给朋友,也不会不小心把真实密码一起发出去。
这行为什么不能少?
volumes:
- postgres_data:/var/lib/postgresql/data
数据库是最不应该“住临时房”的东西。
这行的意思是:把 postgres_data 这只储物柜,接到数据库真正保存资料的位置。这样你重建 PostgreSQL 容器时,数据库内容不会一起消失。
healthcheck 又是什么?
你会看到:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
你可以把它理解成物业在开门前打一通测试电话:数据库不只是房门开了,而是真的已经能接电话、能回答问题。
为什么要做这个检查?因为容器“已经启动”不等于数据库“已经准备完成”。Docker 官方也特别说明,服务启动顺序和服务真正可用是两件事;需要健康检查才能更可靠地等待服务 ready。Docker 官方说明
第 6 步:再看 n8n 房间,它负责“干活”
这部分最常让新手困惑的是:
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
为什么不是:
DB_POSTGRESDB_HOST: localhost
因为你现在不是在电脑浏览器里访问数据库,而是让 n8n 这个容器 去找 PostgreSQL 容器。
在 n8n 房间里,localhost 的意思是“n8n 房间自己”。n8n 如果拨 localhost:5432,其实是在问自己:你这里有没有数据库?当然通常没有。
真正正确的方式,是拨内部电话簿:
DB_POSTGRESDB_HOST: postgres
这里的 postgres 就是前面定义的服务名。它像房间名,也是内部网络里的联系电话。
5432 则是 PostgreSQL 房间内部的接待窗口号码。
记住:浏览器访问 n8n 时,看localhost:5678;n8n 访问 PostgreSQL 时,看postgres:5432。前者是从楼外进来,后者是楼内房间打电话。
第 7 步:为什么只有 n8n 有 ports?
n8n 部分有:
ports:
- "127.0.0.1:5678:5678"
它的意思是:让这台电脑的浏览器可以通过 http://localhost:5678 打开 n8n 。
而 PostgreSQL 没有 ports。
这是故意的,不是漏写。
数据库不需要给浏览器直接打开,它只需要在楼内接 n8n 的电话即可。少开一扇对外的门,就少一份不必要的暴露。
左边的 127.0.0.1 表示先只允许本机访问。对于刚开始学习 Docker 的人,这是一个稳妥的起点。
第 8 步:n8n_data 和 postgres_data,为什么要两只储物柜?
配置底部有:
volumes:
n8n_data:
postgres_data:
这表示两只独立储物柜。
| 储物柜 | 保存谁的资料 | 为什么分开更好 |
|---|---|---|
n8n_data |
n8n 自己的配置、工作流相关数据 | 方便独立维护、检查与迁移。 |
postgres_data |
PostgreSQL 的数据库文件 | 数据库有自己的保存规则,不应该和 n8n 文件混在一起。 |
这就像办公室和财务室各有自己的上锁柜。分开不只是“看起来整洁”,而是为了以后备份、恢复和排查时更清楚。
第 9 步:启动前,先让 Docker 帮你检查配置
不要复制完就直接启动。先在 n8n-docker 文件夹中打开终端,运行:
docker compose config
它像物业在施工前把整份清单重新念一遍。
如果 YAML 缩进错了、变量没找到、冒号写漏了,这一步通常会直接告诉你。比起启动失败后盲猜,先做这一步能省很多时间。
没有明显错误后,再运行:
docker compose up -d
-d 的意思是让它在后台运行。你关掉终端,n8n 也不会因此停止。
接着运行:
docker compose ps
你想看到的不是一大串技术文字,而是两个服务都处于 running 或 Up 状态。
如果 PostgreSQL 还在 starting,给它几十秒准备时间。n8n 设定了等待健康检查,它会在数据库真正准备好之后再继续。
第 10 步:打开 n8n,完成第一次验证
浏览器打开:
http://localhost:5678
如果看到 n8n 初始化页面 ,说明:
- n8n 房间已经建好;
- 大楼外的 5678 门牌已经接通;
- 数据库房间已经准备好;
- n8n 已经拿到内部电话簿并能连接数据库。
请先创建管理员账号,再新建一个测试工作流。然后不要急着做复杂自动化,先让这一套结构稳定运行一天。真正能跑稳的基础,比一次性堆很多节点更重要。
如果你卡住了,先只看这 4 个问题
1. docker compose up -d 直接报错
先运行:
docker compose config
最常见的问题是 YAML 缩进。YAML 对空格很敏感,请统一使用空格,不使用 Tab。
2. n8n 不断重启
运行:
docker compose logs -f n8n
从最早一条 ERROR 开始读。很多问题是 .env 变量没填、数据库地址写错、或者密钥为空。
3. n8n 显示数据库连接失败
先检查:
docker compose logs -f postgres
然后确认 n8n 配置里是否写了:
DB_POSTGRESDB_HOST: postgres
不要写 localhost。
4. 浏览器打不开 localhost:5678
运行:
docker compose ps
确认 n8n 服务是否真的在运行,以及端口是否显示为 127.0.0.1:5678->5678/tcp。如果显示端口被占用,改左边的 5678,例如改为 18080,再访问 http://localhost:18080 。
你现在不需要背住全部配置
你只需要真正记住四句话:
compose.yaml是整套应用的物业清单。.env是放密码和密钥的小信封。postgres是 n8n 在楼内拨打的数据库房间名。数据卷让你重建容器时,不会丢掉成果。
只要这四句话清楚,后面不管你增加 Redis、反向代理、备份工具还是其他应用,都是在这套结构上加新的房间和规则,而不是重新学习一门完全陌生的技术。
下一篇我们解决最后一个让人最焦虑的问题:当 Docker 提示端口冲突、数据库连不上、容器一直重启、数据好像没了时,到底应该先看哪里,才能不靠重装碰运气。