Docker 是什么:给 n8n 新手的最小必要知识
装 n8n 最容易卡住的地方不是 n8n 本身,是它前面那个 Docker。照着教程把五行命令抄进去、容器起来、界面上点两下也能用,第二天回来发现工作流全没了——原因只是那五行里少了一个 -v。
这篇只讲装 n8n 用得到的那部分 Docker:镜像、容器、卷、端口。四个词。至于网络模式、Compose、自己构建镜像,等你真要用再说,提前学也会忘。
前置条件就一条:能在终端敲命令,知道「端口」是什么。没碰过 Docker 完全不影响。
- n8n 为什么最后都装进 Docker
- 四个词:镜像、容器、卷、端口
- 先把 Docker 装上
- 起一个 n8n,三步
- 怎么算装好了
- 数据到底在哪个文件里
- 六个真会遇到的错
- 跟 n8n 有关的几条安全事项
n8n 为什么最后都装进 Docker
官方安装页给了好几条路:npm 全局装、npx 直接跑、Docker、桌面版。前两条最轻,一条 npm install -g n8n 就完事,问题是它把 n8n 和你机器上的 Node 绑死了。n8n 对 Node 版本挑得细,文档写的是 20.19 到 24.x 这个区间。哪天你为别的项目把 Node 升到 26,n8n 跟着罢工,报错还不一定说人话。
Docker 换了思路:把 n8n 和它需要的 Node、系统库、时区数据全打进一个包,这包在谁机器上都一样跑。我特意进 2.43.7 这个镜像里看了一眼,Node 是 26.7.0,基础镜像是 Alpine。这些跟你宿主机上装的是 Node 18 还是 26 一点关系都没有。
代价也说清楚。你多了一层要维护的东西:镜像占盘(2.43.7 解压后 1.8 GB),出问题要多看一处日志,升级从「npm update」变成「换镜像重跑」。我觉得值——升级那一下,改 tag、删容器、重跑,三分钟,不用管 Node 版本。
只在本地玩两天的话,我不建议上 Docker,npm 更快。打算长期跑,或者要给同事用,直接上 Docker,别走弯路。
四个词:镜像、容器、卷、端口
镜像
镜像是别人打好的只读包,里面是一套能跑 n8n 的完整文件系统。n8nio/n8n 是名字,2.43.7 是标签。同一个名字下挂着一堆标签,一个标签对应一个版本。
标签写 latest,意思是「最新稳定版」。生产上我不建议这么写:哪天容器重启顺手拉到新镜像,行为变了,你连从哪儿查都不知道。版本号写死,升级当成一件单独的事做。

docker pull 就是你要敲的容器
镜像是死的。docker run 之后跑起来的那个进程才叫容器。容器等于镜像加一层可写层,你在里面改的任何东西都写在这层上。容器删掉,这层跟着消失——这句话是后面所有坑的源头。
卷
所以 n8n 的数据不能放在容器里。卷是宿主机上的一块目录,由 Docker 管着,挂进容器的某个路径。n8n 认的路径是 /home/node/.n8n,数据库、加密密钥、事件日志都在那儿。
我数了一下:一个刚建完 owner 账号、跑过一次工作流的 .n8n 目录,5.5 MB。其中 database.sqlite 1.5 MB,另有一个 4 MB 的 -wal 文件。就这么点东西,丢了就是全部重来。
端口
-p 5678:5678,冒号左边是宿主机端口,右边是容器内端口。容器里的 n8n 永远监听 5678(除非你改 N8N_PORT),你想在外面换个号,只动左边:-p 8080:5678。
写成 127.0.0.1:5678:5678 就只绑本机,外面连不进来。我测试的时候都用这种写法,省得忘了关。
先把 Docker 装上
Ubuntu / Debian 上最省事的是官方那条脚本:
curl -fsSL https://get.docker.com | sh
它加官方源、装 docker-ce、带 compose 插件。介意「curl 出来的东西直接喂给 sh」的,老老实实走 apt 官方源那套,步骤 Docker 文档里有,就是长一点。
装完敲两个命令看版本:
docker --version
# Docker version 29.1.3, build 29.1.3-0ubuntu3~24.04.2
我这台 232 是 Ubuntu 24.04 + Docker 29.1.3。你用 24、25 都行,命令没有差别。
Docker 默认要 root 或者 sudo。有人说把用户加进 docker 组就不用 sudo 了,这话对,但等于给了那个用户 root 权限,理由在安全那节。
起一个 n8n,三步
第一步:建卷
docker volume create n8n_data
# n8n_data
回显卷名就是建好了。这步不建也行,docker run 里写 -v n8n_data:/home/node/.n8n 会自动创建——不过先建一次,你能确认它真在。
第二步:跑起来
docker run -d
--name n8n
-p 127.0.0.1:5678:5678
-e TZ=Asia/Shanghai
-e GENERIC_TIMEZONE=Asia/Shanghai
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
-v n8n_data:/home/node/.n8n
n8nio/n8n:2.43.7
跟官方文档的原始命令比,我改了两处。
一处是加了 -d。文档给的是 docker run -it --rm,前台跑,终端一关或者 Ctrl+C,n8n 就停;--rm 还顺手把容器删了。调试时这么用很清爽,长期跑不行,加 -d 让它后台待着。
另一处是端口前面补了 127.0.0.1:。文档写的是 -p 5678:5678,那等于绑在 0.0.0.0 上,机器只要有公网 IP,谁扫到 5678 都能看见你的登录页。我这台 232 的 5678 被占着,实际用的是 -p 127.0.0.1:5681:5678,映射关系一样。
两个时区变量都得给。TZ 管容器的系统时间,GENERIC_TIMEZONE 管 n8n 自己怎么算时间,Schedule Trigger 认的是后者。只设 TZ,定时任务还按 UTC 走,你以为的早上 9 点会变成北京时间 17 点。这种事日志里一个异常都不会留。
第三步:看日志,等它起来
docker logs -f n8n
首次启动会跑几十条数据库迁移,Starting migration ... 和 Finished migration ... 交替刷,正常。等到这两行出现:
Version: 2.43.7
Editor is now accessible via:
http://localhost:5678
那个 http://localhost:5678 是容器内部的地址,浏览器里打不开。你能打开的是 http://服务器IP:5678,或者本机上的 http://127.0.0.1:5678。这行字骗过不少人,包括第一次跑的我。
2.43.7 启动时还会吐五条 deprecation 警告,讲 N8N_RUNNERS_MODE、N8N_UNVERIFIED_PACKAGES_ENABLED 这些默认值以后要改。不影响使用,是给未来版本铺路。另外还有一行 Failed to start Python task runner in internal mode. because Python 3 is missing from this system.——镜像里没装 Python,你只用 JS 的 Code 节点就完全不用管它。

建完账号就进去了。这一步不写进卷里就没意义——下面验证会讲怎么确认它真落盘了。
怎么算装好了
四个检查,都在终端里做。
docker ps里 STATUS 是Up,PORTS 有映射,IMAGE 是你指定的那个 tag。curl -s http://127.0.0.1:5678/healthz返回{"status":"ok"}。这个接口只说明进程活着。docker exec n8n n8n --version回2.43.7,跟你给的 tag 对上。docker restart n8n,等十几秒再打开页面,不该再让你建账号。
第 4 条最容易忽略,也最值钱。它一次同时验证两件事:卷挂对了,数据真的在容器外面。

我截这张图的时候顺手看了眼容器里的 .n8n,5.5 MB,跟上面说的对得上。要是你的 du -sh 出来是几百 K,多半数据写进容器可写层了,卷没挂上。
数据到底在哪个文件里
卷建好之后,宿主机的实际位置由 Docker 决定,用这条查:
docker volume inspect n8n_data
# [
# {
# "CreatedAt": "2026-11-02T09:28:41+08:00",
# "Driver": "local",
# "Mountpoint": "/var/lib/docker/volumes/n8n_data/_data",
# "Name": "n8n_data",
# "Scope": "local"
# }
# ]
「Mountpoint」就是它在宿主机上的真实位置。我这台把 Docker 的数据根目录挪到了 /data/docker,所以看到的是 /data/docker/volumes/n8n_data/_data;默认安装的机器上就是上面这个 /var/lib/docker/...。以你自己那台输出为准。
进容器看,就是 n8n 眼里的样子:
docker exec n8n ls -la /home/node/.n8n
# -rw-r--r-- 1 node node 1519616 Nov 2 09:29 database.sqlite
# -rw-r--r-- 1 node node 32768 Nov 2 09:38 database.sqlite-shm
# -rw-r--r-- 1 node node 4136512 Nov 2 09:37 database.sqlite-wal
# -rw-r--r-- 1 node node 7972 Nov 2 09:37 n8nEventLog-1.log
# -rw-r--r-- 1 node node 526 Nov 2 09:38 n8nEventLog.log
工作流、执行记录、凭据密文,全在 database.sqlite 里。解凭据要用的加密密钥在同一个目录,默认是 config 文件,首次启动时自动生成。
所以备份 n8n 就是两样东西:这个目录、还有版本号。只备份 sqlite 不备份密钥,换台机器恢复出来,凭据全是乱码。
想换成自己指定的宿主机目录(比如 /data/n8n),把 -v n8n_data:/home/node/.n8n 换成 -v /data/n8n:/home/node/.n8n 就行。这就是「绑定挂载」,目录归你管,备份更直观,代价是权限要自己摆平——下一个坑就是它。
六个真会遇到的错
1. 忘了 -v,删容器等于删库
我拿两个容器做了对照,同一台机器、同一个镜像:带 -v n8n_data:/home/node/.n8n 的那个,重启后接口返回的「是否显示首次设置」是 false;不带 -v 的另一个,同样的接口返回 true——它又变回一个空实例了。
整个过程不报错,不告警。你只是某天打开发现工作流没了。
判断办法:docker inspect n8n,往上翻找 "Mounts" 那一段,里面出现 "Destination": "/home/node/.n8n" 就说明挂上了;整段是空的,那就危险了。
2. 挂宿主机目录,报 EACCES
想用绑定挂载,目录建好了直接挂,于是:
Error: Failed to load command "start"
Error: EACCES: permission denied, open '/home/node/.n8n/config'
前面还在正常打迁移日志,突然就死在这。因为镜像里的 n8n 不是 root,是 uid 1000 的用户 node(docker exec n8n id 能看到 uid=1000(node)),而你 mkdir 出来的目录属主是 root,755,别人写不进去。
改属主就行:
chown -R 1000:1000 /data/n8n
改完重跑,同一个目录、同一条命令,200,正常起来了,文件也真的落到了宿主机目录里。宿主机上 uid 1000 是谁无所谓,容器里认的就是 1000。
3. 端口被占
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint n8n
(...): failed to bind host port 0.0.0.0:5678/tcp: address already in use
意思是宿主机 5678 已经有人在用了,跟容器内部一点关系都没有。先看是谁:
ss -ltnp 'sport = :5678'
常见情况是你之前那个 n8n 容器还在跑,docker ps 能看到。或者旧容器删了但没删干净,换个端口先跑起来最简单。
4. 用 IP + http 访问,登录完又跳回登录页
页面打得开,账号密码也对,点登录没报错,然后页面回到登录框。看起来像密码错了,其实是 cookie 的问题。
n8n 的登录 cookie 带 Secure 标记(N8N_SECURE_COOKIE 默认就是 true),浏览器只允许它在 HTTPS 下保存。你用 http 访问,服务器照样返回 200,浏览器直接把这个 cookie 扔掉,于是下一个请求又没登录态。
两条路:正经做法是在前面挂一层 Nginx 上证书;内网临时调试,加 -e N8N_SECURE_COOKIE=false,调完记得删掉这行。别把它当长期配置——那等于让你账号密码在明文链路上跑。
5. 关掉终端,n8n 跟着停了
照文档那条 docker run -it --rm 跑的,Ctrl+C 就结束。它不是后台服务,是你终端里的一个前台进程。
补 -d 重跑;已经跑起来的,docker start n8n 也能把它拉回来,但那个容器已经带 --rm 被删了的话,只能重新 docker run——数据在卷里,还在。
6. 用了 latest,某天行为变了
没有报错,没有告警,只是某天重启之后节点的默认值、界面、甚至某个参数名变了。原因是你拉到了新版本。
写死版本号,升级的时候当一件正经事做:先备份卷,再换 tag,起来后跑两个关键工作流看结果。升级本身没什么技术含量,麻烦都在「不知道变了什么」。
跟 n8n 有关的几条安全事项
别把 5678 直接怼到公网。n8n 有登录页,但它是自托管应用,登录之外没有额外防护,扫描器天天在找这种端口。放到 0.0.0.0 上又没反代、没 HTTPS,等于把家钥匙挂在门口。我测试时的写法 -p 127.0.0.1:5678:5678 已经把外面挡住了。
docker 组等于 root。能把 / 挂进容器的人,就能改宿主机上任何文件。给同事开权限之前想清楚这一点。
卷要当密钥对待。里面那个 database.sqlite 存着所有凭据的密文,同一目录里有解密用的密钥。谁把这两样一起拷走,你所有第三方账号的 token 就都是明文了。备份放哪儿,比对备份更重要的。
别在命令行里写密码。docker run -e DB_PASSWORD=xxx 会留在 docker inspect 的输出里,谁执行一下都能看到。要传敏感值就用 env 文件,或者 _FILE 后缀那套变量。
小结
四个词记住就够跑起来:镜像是包,容器是跑起来的包,卷是包外面那块地,端口是门牌号。
装 n8n 真正会出事的地方只有一个:卷没挂。它不报错,你也不会立刻发现,等你发现的时候工作流已经没了。所以装完第一件事是 docker restart 一次,看数据还在不在。
剩下的都是熟练度问题。用熟了你会发现 Docker 最大的好处不是「跨平台」,是「出事可以一把删掉重来」——数据在卷里,容器本来就可以随时扔。
上一篇是n8n 启动报错与端口占用怎么查,那边讲的是起不来的情况。整个系列的目录在这里,故障类的我另整理在故障排查页。
参考资料
- Install with Docker:官方
docker run命令、docker volume create、/home/node/.n8n卷的说明,以及更新镜像的步骤 - Install using Docker Compose:compose 版的配置,
5678:5678那条注释写着「唯一应该对外的端口」 - Deployment 环境变量:
N8N_PORT默认 5678、N8N_LISTEN_ADDRESS默认::、N8N_USER_FOLDER与N8N_ENCRYPTION_KEY - Security 环境变量:
N8N_SECURE_COOKIE默认true、N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS默认false - Docker volumes:卷存在宿主机的
/var/lib/docker/volumes/<名字>/_data,以及它和绑定挂载的区别 - n8nio/n8n 的标签列表:每个版本一个 tag,能查到镜像摘要和压缩体积
- Install Docker Engine on Ubuntu:apt 官方源的完整步骤,不想用一键脚本就走这条
最后更新:2026 年 11 月 2 日,用的是 n8n 2.43.7。

