远程 Mac 上用 launchd 自动化部署 OpenClaw 网关

周五晚上,Webhook 突然全红。你 SSH 进远程 Mac,发现网关进程早就没了——上次是你手动 openclaw gateway run 起的,Mac 凌晨自动重启后没人再敲那条命令。Discord 机器人在线,但 18789 端口空着,GitHub 回调堆了 47 条 502。

这不是 OpenClaw 的 bug,是运维模型还停留在「开发机思维」:交互式启动、plist 手改、每台机器配置各一份。2026 年要把网关当基础设施来管,macOS 上的正解是 launchd——开机自启、崩溃拉起、日志落盘、配置可 Git 化。

本文交付一套从 0 到验收的自动化方案:onboard 注册、生产级 plist 模板、一键 bootstrap 脚本、健康探针与升级回滚顺序。排错见姊妹篇 OpenClaw 网关 launchd 稳定性排错手册;多机 CI 编排见 OpenClaw 手把手部署与 GitHub Actions 自动化

Quick Answer:三个最常见问题

问题 直接答案 注意
最快让网关「重启还在」? openclaw onboard 选 launchd,或跑文末 bootstrap 脚本 doctor 绿再注册,避免 crash loop
手动 gateway run 和 launchd 能并存吗? 不能——会双占端口 改前 launchctl bootout + lsof 清端口
怎么验收「真的高可用」? reboot → 等 2 分钟 → gateway probe + 外网 curl 别只测 localhost

一、为什么必须告别手动配置

在远程常驻 Mac 上,手动配置的典型失败模式如下:

做法 看起来省事 实际代价
SSH 里 nohup openclaw gateway run & 5 秒起服务 重启即失;无日志轮转;退出码不可见
每台机器手改 plist 「就改个端口」 Label 冲突、PATH 漂移、无法 diff 升级
文档写「登录后手动点启动」 绕过 TCC 麻烦 节假日断电恢复必出事
Docker + launchd 双开 「多一层保险」 抢 18789;State 目录双写锁
核心判断:网关是有状态长连接服务,不是一次性脚本。launchd 把「进程监督」交给系统,你把精力放在配置与探针上——这才叫自动化。

二、网关「高可用」在 Mac 上指什么

单机 Mac 做不到 Kubernetes 式多副本,但可以做到运维意义上的 HA

  • Survive reboot:开机 2 分钟内网关自动监听,无需人工 SSH。
  • Survive crash:KeepAlive + ThrottleInterval,进程异常退出后退避重启,不 CPU 打满。
  • Survive config drift:plist、环境变量、~/.openclaw 有备份与 Git 记录。
  • Survive silent failure:外层 healthcheck 发现「端口在但 probe 失败」。
  • Survive upgrade:backup → doctor --fix → gateway restart 固定顺序,可脚本化。

实测:一台 M4 Mac mini(24GB)跑 OpenClaw 网关 + 轻量插件,空闲功耗约 4–6W;网关进程内存通常 200–450MB。把机器放在 远程 Mac 原生部署路径上,比在办公室工位留一台 iMac 常亮更可控。

三、LaunchAgent 还是 LaunchDaemon

维度 LaunchAgent(用户域) LaunchDaemon(系统域)
路径 ~/Library/LaunchAgents/ /Library/LaunchDaemons/
启动时机 用户登录后 系统启动,无需 GUI 登录
TCC / 钥匙串 可继承用户授权 受限,适合纯网络守护
推荐场景 需浏览器自动化、插件读用户目录 纯 HTTP/Webhook 网关、无 GUI 依赖

2026 默认建议:先用 LaunchAgent 跑通 onboarddoctor;确认不需要登录会话后,再迁 Daemon 并写进 Runbook。无论哪种,ProgramArguments 必须用绝对路径——launchd 不读你的 .zshrc

四、onboard 一键注册 launchd

OpenClaw 的 onboard 向导会把 Node 路径、State 目录、网关端口写进配置,并在较新版本里支持直接安装 launchd 服务。推荐顺序:

# 1. 确认运行时(与 plist 内 PATH 一致)
node -v          # 期望 v22.x
which openclaw   # 记下绝对路径,如 /opt/homebrew/bin/openclaw

# 2. 预检
openclaw doctor

# 3. 交互式 onboard(按提示启用 Gateway + launchd)
openclaw onboard

# 4. 验收
launchctl print "gui/$(id -u)/com.openclaw.gateway" 2>/dev/null || launchctl list | grep -i openclaw
lsof -nP -iTCP:18789 -sTCP:LISTEN
openclaw gateway probe
口诀:交互 SSH 跑通 → onboard 固化 → reboot 验收。跳过第二步直接 KeepAlive,只会得到更快的 crash loop。

五、生产级 plist 模板(可进 Git)

onboard 未生成 plist,或你需要在 infra 仓库里显式版本化,可用下面模板。把 OPENCLAW_BIN 换成 which openclaw 的输出:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.openclaw.gateway</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/openclaw</string>
    <string>gateway</string>
    <string>run</string>
  </array>
  <key>WorkingDirectory</key>
  <string>/Users/your-ci-user</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    <key>HOME</key>
    <string>/Users/your-ci-user</string>
  </dict>
  <key>StandardOutPath</key>
  <string>/Users/your-ci-user/Library/Logs/OpenClawGateway/gateway.stdout.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/your-ci-user/Library/Logs/OpenClawGateway/gateway.stderr.log</string>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <dict>
    <key>SuccessfulExit</key>
    <false/>
  </dict>
  <key>ThrottleInterval</key>
  <integer>30</integer>
</dict>
</plist>

加载(Ventura+):

UID_NUM=$(id -u)
DOMAIN="gui/${UID_NUM}"
LABEL="com.openclaw.gateway"
PLIST="${HOME}/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "${DOMAIN}/${LABEL}" 2>/dev/null || true
launchctl bootstrap "${DOMAIN}" "${PLIST}"
launchctl kickstart -k "${DOMAIN}/${LABEL}"

别踩的坑:

  • Intel Mac 上 Homebrew 常在 /usr/local/bin,Apple Silicon 在 /opt/homebrew/bin——plist 里写死,别写「通用 PATH」。
  • 日志目录先 mkdir -p,否则 launchd 可能起不来且 stderr 无处可查。
  • 同一 Label 不要同时存在于用户域与系统域;升级前先 bootout 旧 job。

六、一键 bootstrap 脚本

把「装 CLI → doctor → 写 plist → bootstrap → probe」收成一条命令,适合新租的云 Mac 或 GitHub Actions 自托管节点。完整脚本见本文资源目录:

resources/bootstrap-openclaw-gateway-launchd.sh

在远程 Mac 上执行:

chmod +x bootstrap-openclaw-gateway-launchd.sh
./bootstrap-openclaw-gateway-launchd.sh

脚本逻辑摘要:

  1. 检测 node / openclaw,缺失则 npm i -g openclaw@latest
  2. 创建 ~/Library/Logs/OpenClawGateway
  3. 优先 openclaw onboard;若无 plist 则写入兜底 LaunchAgent
  4. launchctl bootstrap + kickstart -k
  5. lsof + openclaw gateway probe 验收
团队用法:脚本进 infra 仓库,每台 Mac 只注入 secrets(API token、区域)。新机器 5 分钟内从裸机到 probe 绿——这才是「不要再手动配置」。

七、健康探针与自愈外层

KeepAlive 只能管进程在不在,管不了「进程僵死但端口仍 LISTEN」。加一层轻量探针 LaunchAgent(每 5 分钟):

#!/bin/bash
# ~/bin/openclaw-gateway-healthcheck.sh
set -euo pipefail
PORT="${OPENCLAW_GATEWAY_PORT:-18789}"
LABEL="com.openclaw.gateway"
DOMAIN="gui/$(id -u)"

if ! openclaw gateway probe >/dev/null 2>&1; then
  logger -t openclaw-ha "probe failed, kickstart gateway"
  launchctl kickstart -k "${DOMAIN}/${LABEL}" || true
fi

配合 StartCalendarInterval 的 plist 挂到 ~/Library/LaunchAgents/com.openclaw.gateway-healthcheck.plist。外层还可接 UptimeRobot、自建 Prometheus blackbox——但探针一定要走与 Webhook 相同的路径(含反代与 TLS),别只 curl localhost。

日志轮转建议用 newsyslog 或按大小 truncate,避免单文件撑满 NVMe——磁盘满时 launchd 子进程会以 ENOSPC 退出,表现像「随机掉线」。

八、配置 Git 化与滚动升级

把下面文件纳入版本控制,PR 里 review 而不是 SSH 手改:

文件 用途
launchagents/com.openclaw.gateway.plist 网关主服务
scripts/bootstrap-openclaw-gateway-launchd.sh 新节点引导
scripts/upgrade-openclaw-gateway.sh 固定升级顺序
docs/runbook-gateway.md on-call 手册

升级脚本最小顺序(与 多通道网关稳定性姊妹篇一致):

openclaw backup create
npm update -g openclaw@latest   # 或锁版本号
openclaw doctor --fix
launchctl kickstart -k "gui/$(id -u)/com.openclaw.gateway"
openclaw gateway probe
# 多插件环境:逐个 browser/cron doctor

九、七步验收清单

发版或新机器上线前,SSH 执行并打勾:

  • openclaw doctor 无 ERROR
  • launchctl print gui/$(id -u)/com.openclaw.gateway 状态为 running
  • lsof -nP -iTCP:18789 -sTCP:LISTEN PID 与 plist 一致
  • openclaw gateway probe 成功
  • 从办公网 / 手机网络 curl 公网入口(非仅 127.0.0.1)
  • sudo reboot 后 2 分钟内 probe 仍成功
  • 故意 kill 网关 PID,30 秒内自动恢复且 Throttle 不刷屏

十、为什么仍推荐 Mac mini 跑这套自动化

网关要长期在线、低噪音、路径统一Mac mini M4 在 Apple Silicon 上 idle 约 4W,7×24 比台式机省电一个数量级;launchd、Homebrew、OpenClaw 的 ~/.openclaw 与本地开发机完全一致,Runbook 不用改两套。

若你正在把 OpenClaw 从「个人笔记本上的实验」迁到独享远程 Mac,建议单机跑顺自动化再加节点。Macstripe 首页 可按天试用各区域 Mac mini,把 bootstrap 脚本跑一遍,比在办公室留一台机器常亮更省心。

FAQ

OpenClaw 网关一定要用 launchd 吗?

不是必须,但 macOS 上 launchd 是官方守护进程管理器,比 nohup 更适合无人值守。Docker 适合多版本并行;launchd 适合少一层虚拟化、低延迟网关。

LaunchAgent 和 LaunchDaemon 怎么选?

需要用户 TCC 或钥匙串时用 LaunchAgent;必须无人登录即监听时用 LaunchDaemon(root)。多数团队从 Agent 起步。

onboard 和手写 plist 会冲突吗?

Label 必须唯一。升级前先 bootout,避免双进程抢 18789。最终 plist 建议进 Git。

probe 失败但端口在监听?

查鉴权、TLS、绑定地址。先 doctor,再对照 ~/.openclaw;细节见 launchd 排错手册

多台 Mac 怎么批量部署?

自托管 Runner + 同一 bootstrap 脚本,secrets 管令牌,滚动 probe 验收。参见 GitHub Actions 多机协作

总结

不要再手动配置的意思,不是「永远不碰终端」,而是终端里只跑脚本,不跑即兴命令onboard 注册 launchd、plist 进 Git、bootstrap 上新机、probe 验收、healthcheck 兜底。网关从「某次 SSH 会话里的前台进程」变成「可重复的基础设施」。

下一步:在一台远程 Mac 上跑通 bootstrap,做一次 reboot 演练,把七步清单贴进团队 Wiki。需要独享节点时,从 Macstripe 首页 选区域即可。