NAS 上用 Docker 部署 OpenClaw
在 NAS 的 Docker 里跑 OpenClaw 网关:初始化配置、持久化状态目录、docker compose 编排,以及内网访问时该开哪些开关、不该开哪些。
NAS 常年开机、盘位充足,很适合当常驻服务的宿主。OpenClaw 网关就是一个典型的常驻进程:它需要持久化的状态目录、一个稳定的端口,以及重启后能自己回来的编排方式。
下面这套是单容器的最小可用配置,跑在群晖/威联通这类带 Docker 的 NAS 上。
一、先跑一次初始化
首次启动要先跑 onboard 生成配置,跑完容器退出,配置留在挂载出来的目录里。
docker run -it --rm \
--user 1000:1000 \
-e HOME=/home/node \
-e OPENCLAW_STATE_DIR=/home/node/.openclaw \
-e OPENCLAW_CONFIG_PATH=/home/node/.openclaw/openclaw.json \
-e OPENCLAW_GATEWAY_TOKEN='<随机令牌>' \
-v /vol1/1000/docker/openclaw:/home/node/.openclaw \
-v /vol1/1000/docker/openclaw-auth:/home/node/.config/openclaw \
alpine/openclaw \
node dist/index.js onboard --mode local --no-install-daemon
三个要点:
--user 1000:1000要和宿主机目录的属主对齐,否则容器写不进挂载点。群晖的普通用户通常是1026,用id查一下再改。OPENCLAW_GATEWAY_TOKEN不要留空。空值等于没有鉴权,任何能碰到这个端口的人都能接管你的网关。用openssl rand -hex 32生成一个。- 两个卷要分开挂:
/home/node/.openclaw是状态和 workspace,/home/node/.config/openclaw是 auth profile 密钥。混在一起的话,备份状态目录时会连密钥一起带走。
二、配置文件里的网关段
初始化完,openclaw.json 里至少要有这一段:
{
"gateway": {
"mode": "local",
"auth": {
"mode": "token",
"token": "<与上面 OPENCLAW_GATEWAY_TOKEN 相同的值>"
},
"port": 18789,
"bind": "lan",
"tailscale": {
"mode": "off",
"resetOnExit": false
}
}
}
bind: "lan" 是为了让同网段的设备能连上 Control UI。如果只在 NAS 本机用,改成 "loopback" 更安全。
三、关于两个「危险开关」
网上不少教程会建议在内网部署时加上这两行,让手机、平板不用配对就能登录:
{
"gateway": {
"controlUi": {
"allowInsecureAuth": true,
"dangerouslyDisableDeviceAuth": true
}
}
}
它们的实际效果是:绕过设备配对,允许非 HTTPS 来源认证。只有在完全隔离的内网、且你清楚谁在用这个网关时才开。一旦端口被映射到公网、或者 NAS 上跑了反向代理把它暴露出去,这两项就等于把门锁拆了。默认配置里不要带。
我的建议是:先按默认(不带这两项)部署,用手机走一次正常的设备配对流程。配对是一次性的,配完就不用再管,没必要为此长期削弱鉴权。
四、docker compose 编排
初始化跑通之后,用 compose 接管日常启动:
services:
openclaw-gateway:
image: ${OPENCLAW_IMAGE:-alpine/openclaw}
container_name: openclaw-gateway
restart: unless-stopped
init: true
environment:
HOME: /home/node
OPENCLAW_HOME: /home/node
# 容器内路径固定,避免宿主机路径泄漏进运行时
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
OPENCLAW_GATEWAY_PORT: "18789"
TZ: ${TZ:-Asia/Shanghai}
# 可选:模型 Key(也可以只写进 openclaw.json)
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
volumes:
# 状态 + 配置 + workspace,全部持久化到 NAS
- ${OPENCLAW_DATA_DIR:-/vol1/1000/docker/openclaw}:/home/node/.openclaw
# auth profile 密钥目录,别和状态目录混在一起
- ${OPENCLAW_AUTH_DIR:-/vol1/1000/docker/openclaw-auth}:/home/node/.config/openclaw
ports:
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789" # Control UI + Gateway API
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790" # Bridge(可选)
# - "3978:3978" # 只有接 MS Teams 才开
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
extra_hosts:
- "host.docker.internal:host-gateway"
command:
["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18789"]
healthcheck:
test: ["CMD", "node", "dist/docker-healthcheck.js"]
interval: 30s
timeout: 5s
retries: 5
start_period: 60s
cap_drop 和 no-new-privileges 这两项建议保留。网关本身不需要 NET_RAW/NET_ADMIN,去掉能少一个被利用的面。start_period: 60s 是给首次初始化留的余量,老 NAS 的盘慢,别设太短。
五、验证
起来之后先确认容器健康,再确认端口只在预期的网段可达:
docker compose ps
curl -s http://<NAS内网IP>:18789/ -o /dev/null -w "%{http_code}\n"
返回 200 说明 Control UI 已经能访问。然后从路由器层面确认 18789 没有做公网端口映射——这是这套部署里最关键的一步。