OpenClaw 多台 Docker 主机 Node 节点部署实录
记录在 Ubuntu ARM64 云主机和 Linux Docker 主机上部署 OpenClaw Node 的完整过程,包括公网 WSS 直连、节点配对、systemd 持久化,以及从 SSH 隧道迁移到公网连接的实践经验。
这次的目标,是让多台运行 Docker 的 Linux 主机都成为 OpenClaw Gateway 的远程 Node,由统一的 Gateway 管理各台机器上的容器和系统任务。
一、最终架构
整体采用一台 Gateway 管理多台 Node 的结构:
OpenClaw Gateway │ ├── Docker PC Node │ └── Ubuntu ARM64 Node两台 Node 都通过公网 WSS 连接 Gateway:
Node │ └── wss://<gateway-domain>:443 │ ▼ Caddy │ ▼ Relay │ ▼ OpenClaw GatewayNode 只需要主动访问 Gateway 的 HTTPS 端口,不需要在 Node 所在机器上开放 OpenClaw 入站端口,也不需要为 Node 做入站端口转发。
二、为什么选择公网 WSS
最开始,Docker PC 使用的是 SSH 本地端口转发:
Node 127.0.0.1:18789 │ └── SSH 隧道 │ ▼ Gateway 127.0.0.1:18789这种方式安全可靠,但需要同时维护 OpenClaw Node 服务和 SSH/autossh 隧道服务。后来改为公网 WSS:
Node → WSS :443 → Caddy → Relay → Gateway这样每台 Node 只需要维护一个 openclaw-node.service,结构更简单,也更适合多台机器统一管理。
公网 WSS 不等于直接裸露 Gateway 端口。Gateway 仍然只监听本机回环地址,由 Caddy 和 relay 负责转发,并通过 TLS 和 Gateway 鉴权保护连接。
三、Gateway 侧准备反代
Gateway 本身监听本机地址:
127.0.0.1:18789由于 Caddy 运行在 Docker 网络中,无法直接访问 Gateway 的回环地址,因此增加一个本机 relay,将 Docker 网关地址上的端口转发到 Gateway:
[Unit]Description=OpenClaw Gateway relayAfter=network-online.targetWants=network-online.target
[Service]Type=simpleExecStart=/usr/bin/socat TCP-LISTEN:18789,bind=<docker-gateway-ip>,reuseaddr,fork TCP:127.0.0.1:18789Restart=alwaysRestartSec=2
[Install]WantedBy=multi-user.targetCaddy 反代到 relay 地址:
<gateway-domain> { reverse_proxy <docker-gateway-ip>:18789}一个容易踩坑的地方
如果 relay unit 写着:
Requires=openclaw-gateway.service而 Gateway 后来改成用户级 systemd 服务,系统级的 openclaw-gateway.service 不再存在,relay 就可能被连带停止。此时应删除失效依赖,只保留网络依赖:
After=network-online.targetWants=network-online.target然后重新加载并启动:
systemctl daemon-reloadsystemctl enable --now openclaw-caddy-relay.service验证:
systemctl status openclaw-caddy-relay.servicess -tlnp | grep 18789四、处理反向代理身份校验
Gateway 经过 Caddy 和 relay 后,会看到代理转发的客户端请求头。新版 Gateway 对客户端来源识别更加严格,如果没有配置可信代理,可能出现:
proxy_attribution_required在 Gateway 配置中加入严格的本机可信代理范围:
{ "gateway": { "trustedProxies": [ "127.0.0.1", "::1" ] }}配置修改后重启 Gateway:
systemctl --user restart openclaw-gateway.service可信代理范围应尽可能小,不要使用允许任意来源的宽泛网段,避免不可信请求伪造客户端身份。
五、在新 Ubuntu ARM64 主机上安装 Node
本次新主机运行 Ubuntu LTS ARM64,使用 root 用户的用户级安装方式。推荐目录结构如下:
/root/.nvm/ └── Node.js 24.x
/root/.config/systemd/user/ └── openclaw-node.service
/root/.openclaw/ └── Node 身份和运行状态OpenClaw 版本应尽量与 Gateway 保持一致:
openclaw --version确认版本后,查看 Node 启动参数:
openclaw node run --help六、首次使用公网 WSS 连接
首次连接不要直接把长期 Gateway Token 写进命令。推荐在 Gateway 的 Control UI 中进入:
Devices → Pair device → Node host生成一次性的 Node 配对命令,然后直接复制到远程主机终端执行:
openclaw node run --pair "oc-pair://<one-time-setup-code>"配对链接具有以下特点:
- 有效时间有限
- 只能用于首次加入
- 不应该写入 systemd 服务参数
- 不应该发送到聊天窗口、工单或公开日志
- 配对完成后,Node 会保存自己的长期设备凭据
每台机器都必须使用独立的 Node 身份,不能复制其他机器的状态文件或密钥。
如果直接使用普通启动命令而没有认证信息,通常会看到:
unauthorized: gateway token missing这表示网络已经到达 Gateway,但缺少首次连接所需的认证信息,不是端口不通。
七、Gateway 侧批准 Node
首次连接后,在 Gateway 上查看节点状态:
openclaw nodes status新节点可能显示为:
paired · connected · pending approval或者:
reapproval pending使用 Gateway 返回的精确请求 ID 批准:
openclaw nodes approve <request-id>批准后重新检查:
openclaw nodes status预期状态:
paired · connected · approved这里要区分两个概念:
- 设备配对:允许 Node 连接 Gateway
- 能力审批:决定 Node 可以暴露哪些命令和能力
配对成功并不代表已经获得 Docker 或 Shell 管理权限。
八、让 Node 变成持久化服务
首次前台运行验证成功后,使用 OpenClaw 自己的安装命令生成用户级服务:
openclaw node install \ --force \ --host <gateway-domain> \ --port 443 \ --tls \ --display-name "Ubuntu ARM64"启动并设置自启:
systemctl --user daemon-reloadsystemctl --user enable --now openclaw-node.service检查服务和连接日志:
systemctl --user status openclaw-node.service --no-pagerjournalctl --user -u openclaw-node.service --no-pager -n 20正常情况下应看到:
node host gateway connected: wss://<gateway-domain>:443九、root 用户的 systemd lingering
如果 Node 使用 root 用户级 systemd,需要确认用户退出 SSH 后用户级 systemd 不会被销毁:
loginctl enable-linger rootloginctl show-user root -p Linger预期:
Linger=yes最终 Node 服务位于:
/root/.config/systemd/user/openclaw-node.service不要手工把它写进 /etc/systemd/system/,用户级 Node 服务应由 openclaw node install 自动生成和维护。
十、Docker 权限验证
Node 显示在线还不够,还要验证实际命令执行能力。在 Gateway 侧通过 Node 执行:
docker psdocker compose lsdocker versionhostnameuname -a如果能返回目标主机上的容器列表,说明 Node 连接、能力审批、远程执行链路和操作系统 Docker 权限都正常。
十一、把旧 Node 从 SSH 隧道迁移到公网 WSS
原来的 Docker PC 使用 SSH 隧道,迁移时不需要删除设备,也不需要重新配对。
迁移前
openclaw-node.service → 127.0.0.1:18789 → SSH 隧道 → Gateway迁移后
openclaw-node.service → wss://<gateway-domain>:443 → Caddy → Relay → Gateway先确认当前身份和服务:
openclaw node status停止 Node 和旧 SSH 隧道:
systemctl --user stop openclaw-node.servicesudo systemctl disable --now openclaw-tunnel.service确认本地转发端口释放:
ss -tlnp | grep 18789 || echo "本地 18789 已释放"重新生成 Node 服务:
openclaw node install \ --force \ --host <gateway-domain> \ --port 443 \ --tls \ --display-name "Docker PC"启动服务:
systemctl --user daemon-reloadsystemctl --user enable --now openclaw-node.service确认日志:
journalctl --user -u openclaw-node.service --no-pager -n 20如果看到:
node host gateway connected: wss://<gateway-domain>:443说明迁移完成。
迁移过程中不需要做的事
- 不要删除原来的 Node 设备
- 不要删除 Node 身份文件
- 不要重新生成节点 ID
- 不要复制其他 Node 的密钥
- 不要删除执行审批配置
- 不要同时运行旧 SSH 隧道和新 Node 服务
因为连接地址改变,不代表节点身份改变。
十二、最终检查清单
Gateway 侧
systemctl --user is-active openclaw-gateway.servicesystemctl is-active openclaw-caddy-relay.serviceopenclaw nodes statusNode 侧
systemctl --user is-active openclaw-node.servicesystemctl --user is-enabled openclaw-node.serviceloginctl show-user root -p Lingerjournalctl --user -u openclaw-node.service --no-pager -n 20远程执行
hostnamedocker psdocker compose ls预期结果
Gateway: activeRelay: activeNode: paired · connected · approvedNode service: active + enabledLinger: yesDocker command: successful总结
这次部署的关键经验有三点。
第一,公网 WSS 直连比 SSH 本地端口转发更容易扩展。每台 Node 只维护一个用户级 openclaw-node.service,不需要额外维护 autossh 隧道。
第二,Node 的“能连接”和“能管理”是两回事。必须同时完成:
网络连接 + 设备配对 + 能力审批 + 操作系统权限第三,Node 迁移连接地址时,不需要删除原设备。只要保留原有 Node 身份,重新生成服务配置即可继续使用原来的审批记录。
最终,多台 Linux Docker 主机都可以作为 OpenClaw Node 接入统一 Gateway,由 Gateway 集中管理各台主机上的容器和系统任务。