快速开始

  • 进入空白目录,创建 .env、Dockerfile、searxng-settings.yml、docker-compose.yml
  • 以下配置可以直接复制粘贴(使用 加速镜像源)
  • 启动指令 docker compose up -d
  • 访问 http://localhost:5678/
  • 创建一个管理员账号,邮箱需要能收邮件
    • 填写大模型信息,Provider 选择最后一个自定义
      • Base URL、API key、Model
  • 激活地址:http://localhost:5678/settings/usage
    • 粘贴你的邮箱的 KEY
  • 点击 Personal 创建你的工作流(Workflows)或智能体(Agents)
1
2
3
4
5
n8n/
├── .env
├── Dockerfile
├── searxng-settings.yml
└── docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# .env

# n8n version - get the version number of the latest stable release from https://github.com/n8n-io/n8n/releases
N8N_VERSION=latest
# 构建 n8n 定制镜像(含 ffmpeg)所用的 Alpine 基础版本,需与 n8n 官方镜像保持同一大版本
ALPINE_TAG=3.24

# Sandbox service version - the API, runner, and sandbox images share one release.
# Get the number of the latest stable "service" release (not a staging prerelease) from https://github.com/n8n-io/n8n-sandbox-service/releases
N8N_SANDBOX_VERSION=latest

# n8n task runner authentication. Use a random secret you generate.
N8N_RUNNERS_AUTH_TOKEN=change-me-runner-auth-token

# Sandbox service secrets — pick your own values
# API 端:接受 n8n 调用所用的 key 列表
SANDBOX_API_KEYS=change-me-api-key
# API 端:校验 runner 注册用的 token
SANDBOX_API_RUNNER_REGISTRATION_TOKEN=change-me-registration-token
# API 端:调用 runner 时使用的 key
SANDBOX_API_RUNNER_API_KEY=change-me-runner-key
# Runner 端:注册到 API 用的 token(须与上面 API 端的注册 token 一致)
SANDBOX_RUNNER_REGISTRATION_TOKEN=change-me-registration-token
# Runner 端:接受 API 调用所用的 key(须与 SANDBOX_API_RUNNER_API_KEY 一致)
SANDBOX_RUNNER_API_KEYS=change-me-runner-key
# n8n 端:必须与 SANDBOX_API_KEYS 中的某个值完全一致
N8N_SANDBOX_SERVICE_API_KEY=change-me-api-key
# n8n 端:同一 Docker 网络内按服务名访问 sandbox-api
N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080
N8N_INSTANCE_AI_SANDBOX_ENABLED=true
N8N_INSTANCE_AI_SANDBOX_PROVIDER=n8n-sandbox

# Web search: secret for the bundled SearXNG instance — pick your own value
SEARXNG_SECRET=change-me-searxng-secret
N8N_INSTANCE_AI_SEARXNG_URL=http://searxng:8080
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# Dockerfile

# syntax=docker/dockerfile:1
#
# 给 n8n 官方镜像补上 ffmpeg,做成可重建、可持久化的本地镜像。
#
# 为什么需要多阶段:
# n8nio/n8n 官方镜像基于 Alpine,但用的是「Docker Hardened Image」变体,
# apk 包管理器已被剥离,因此无法在该镜像内直接 apk add ffmpeg。
# 解法是在一个完整的 alpine 镜像里装好 ffmpeg,再只把「缺失的」动态库
# 复制进 n8n 镜像,绝不覆盖 n8n 自带的库(musl、libssl、libstdc++ 等)。
#
# 为什么不用 BtbN 的 static 包:
# 实测该二进制仍是动态链接 glibc(带 PT_INTERP,依赖 libmvec.so.1),
# 在 musl 环境下会报 Error loading shared library,无法运行。
# 只有 Alpine 原生 apk 包的 musl 链接版本可以在本镜像里跑。

ARG ALPINE_TAG=3.24
ARG N8N_VERSION=latest

# ---------- 阶段一:在完整 Alpine 里安装 ffmpeg,并导出「二进制 + 全部动态库」 ----------
FROM alpine:${ALPINE_TAG} AS builder

RUN set -eux; \
apk add --no-cache ffmpeg; \
# 递归收集依赖:ldd 本身递归,但再跑几轮补齐传递依赖,避免漏库
: > /tmp/paths.txt; \
for bin in /usr/bin/ffmpeg /usr/bin/ffprobe; do \
ldd "$bin" 2>/dev/null | awk '{print $3}' | grep '^/' >> /tmp/paths.txt || true; \
done; \
for pass in 1 2 3; do \
sort -u /tmp/paths.txt -o /tmp/paths.txt; \
while read -r p; do \
[ -f "$p" ] && ldd "$p" 2>/dev/null | awk '{print $3}' | grep '^/' >> /tmp/paths.txt || true; \
done < /tmp/paths.txt; \
done; \
printf '%s\n' /usr/bin/ffmpeg /usr/bin/ffprobe >> /tmp/paths.txt; \
sort -u /tmp/paths.txt -o /tmp/paths.txt; \
# 只打包 /usr/bin 与 /usr/lib、/lib 下的内容,保留原目录结构
# -h 把符号链接实体化,这样解包后拿到的是真实文件而非指向不存在目标的链接
tar -czhf /ffmpeg-bundle.tgz -T /tmp/paths.txt; \
echo "本次打包 $(wc -l < /tmp/paths.txt) 个路径"; \
ls -lh /ffmpeg-bundle.tgz

# ---------- 阶段二:装进 n8n 官方镜像 ----------
FROM n8nio/n8n:${N8N_VERSION}

USER root

COPY --from=builder /ffmpeg-bundle.tgz /tmp/ffmpeg-bundle.tgz

RUN set -eux; \
mkdir -p /tmp/ffext; \
tar -xzf /tmp/ffmpeg-bundle.tgz -C /tmp/ffext; \
# 1) 安装 ffmpeg / ffprobe 到已在 PATH 中的目录
cp -f /tmp/ffext/usr/bin/ffmpeg /usr/local/bin/ffmpeg; \
cp -f /tmp/ffext/usr/bin/ffprobe /usr/local/bin/ffprobe; \
chmod 755 /usr/local/bin/ffmpeg /usr/local/bin/ffprobe; \
# 2) 安装依赖库;目标已存在则跳过 —— 保护 n8n 自带库不被降级覆盖
cd /tmp/ffext/usr/lib; \
added=0; skipped=0; \
find . -type f | while read -r f; do \
rel="${f#./}"; \
if [ -e "/usr/lib/$rel" ]; then \
skipped=$((skipped + 1)); \
else \
mkdir -p "/usr/lib/$(dirname "$rel")"; \
cp -f "$f" "/usr/lib/$rel"; \
added=$((added + 1)); \
fi; \
done; \
echo "库文件已处理完毕"; \
rm -rf /tmp/ffext /tmp/ffmpeg-bundle.tgz

# ---------- 阶段三:以运行用户身份做真实验证,构建期即暴露兼容问题 ----------
USER node

RUN set -eux; \
command -v ffmpeg; \
ffmpeg -version | head -1; \
ffprobe -version | head -1; \
# 真实转码,而不是只看 --version(后者不加载编码器插件)
ffmpeg -hide_banner -loglevel error \
-f lavfi -i testsrc=duration=1:size=160x120:rate=5 \
-c:v libx264 -y /tmp/_selftest.mp4; \
rm -f /tmp/_selftest.mp4; \
echo "ffmpeg 自检通过"

# 其余 ENTRYPOINT / CMD 继承自官方镜像,不做改动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# docker-compose.yml

volumes:
n8n-data:
sandbox-tls:

services:
sandbox-certs:
image: n8nio/n8n-sandbox-service-api:${N8N_SANDBOX_VERSION}
user: '0:0'
entrypoint: ['sh', '-c']
command:
- >
bootstrap-mtls.sh --out-dir /tls --api-san sandbox-api
--control-san-prefix sandbox-runner --world-readable &&
chown -R sandbox-api:sandbox-api /tls/api && chmod -R a+rX /tls
environment:
NUM_RUNNERS: '1'
volumes:
- sandbox-tls:/tls

sandbox-api:
image: n8nio/n8n-sandbox-service-api:${N8N_SANDBOX_VERSION}
depends_on:
sandbox-certs:
condition: service_completed_successfully
env_file: .env
environment:
SANDBOX_API_GRPC_TLS_CERT_FILE: /tls/api/grpc-server.crt
SANDBOX_API_GRPC_TLS_KEY_FILE: /tls/api/grpc-server.key
SANDBOX_API_GRPC_TLS_CLIENT_CA_FILE: /tls/api/ca.crt
SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CA_FILE: /tls/api/ca.crt
SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/api/control-grpc-api-client.crt
SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/api/control-grpc-api-client.key
SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_SERVER_NAME: sandbox-runner-1
volumes:
- sandbox-tls:/tls:ro
healthcheck:
test: ['CMD', 'wget', '-qO-', 'http://localhost:8080/healthz']
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
# Never publish 8080/9090 to the host on an internet-facing server.
# n8n reaches this container by service name, over the default Compose network.

sandbox-runner-1:
image: n8nio/n8n-sandbox-service-runner-dind:${N8N_SANDBOX_VERSION}
privileged: true
depends_on:
sandbox-api:
condition: service_healthy
env_file: .env
environment:
SANDBOX_RUNNER_API_GRPC_ADDR: sandbox-api:9090
SANDBOX_RUNNER_HTTP_BASE_URL: https://sandbox-runner-1:8080
SANDBOX_RUNNER_CONTROL_GRPC_LISTEN_ADDR: ':9091'
SANDBOX_RUNNER_CONTROL_GRPC_ADVERTISE_ADDR: sandbox-runner-1:9091
SANDBOX_RUNNER_ID: runner-1
SANDBOX_RUNNER_DOCKER_SANDBOX_IMAGE: n8nio/n8n-sandbox-service-sandbox:${N8N_SANDBOX_VERSION}
SANDBOX_RUNNER_REGISTRATION_GRPC_CA_FILE: /tls/runner/ca.crt
SANDBOX_RUNNER_REGISTRATION_GRPC_CERT_FILE: /tls/runner/grpc-client.crt
SANDBOX_RUNNER_REGISTRATION_GRPC_KEY_FILE: /tls/runner/grpc-client.key
SANDBOX_RUNNER_REGISTRATION_GRPC_SERVER_NAME: sandbox-api
SANDBOX_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/runner/control-grpc-server.crt
SANDBOX_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/runner/control-grpc-server.key
SANDBOX_RUNNER_CONTROL_GRPC_TLS_CLIENT_CA_FILE: /tls/runner/ca.crt
volumes:
- sandbox-tls:/tls:ro
# Never expose this container's ports publicly — it runs privileged Docker-in-Docker.

n8n:
# 由本地 Dockerfile 构建:基于官方镜像,额外装入 ffmpeg(见 Dockerfile 顶部说明)
build:
context: .
dockerfile: Dockerfile
args:
N8N_VERSION: ${N8N_VERSION}
ALPINE_TAG: ${ALPINE_TAG}
image: n8nio/n8n:ffmpeg-${N8N_VERSION}
depends_on:
sandbox-api:
condition: service_healthy
ports:
- '5678:5678' # the only port that should be internet-facing
env_file: .env
volumes:
- n8n-data:/home/node/.n8n
- ./data:/home/data
environment:
- N8N_RUNNERS_ENABLED=false
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
- N8N_DEFAULT_BINARY_DATA_MODE=filesystem
- N8N_NATIVE_PYTHON_RUNNER=true
- N8N_RESTRICT_FILE_ACCESS_TO= # 设为空值
- N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES=false # 配合使用,确保不拦截n8n自身目录[reference:5]

runners:
image: n8nio/runners:${N8N_VERSION}
depends_on:
- n8n
environment:
N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
N8N_RUNNERS_TASK_BROKER_URI: http://n8n:5679
# Idle runners exit and are relaunched on demand (per the task-runners docs)
N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT: '15'
N8N_RUNNERS_TASK_TIMEOUT: 600
# Runs user code from Code nodes. Never publish this container's ports.

searxng:
image: searxng/searxng:latest
environment:
SEARXNG_SECRET: ${SEARXNG_SECRET}
volumes:
- ./searxng-settings.yml:/etc/searxng/settings.yml:ro
# Internal-only: n8n reaches it by service name. Never publish its port.
1
2
3
4
5
6
7
# searxng-settings.yml

use_default_settings: true
search:
formats:
- html
- json