
1. OpenClaw 启动后自动发飞书通知为什么值得折腾OpenClaw 这类自托管 Agent 网关最让人心里没底的不是它跑不起来而是它「悄悄挂了你还不知道」。我自己的场景很典型一台常驻的 Linux 小主机跑着 OpenClaw Gateway平时用飞书跟它对话、派任务。有段时间机器重启后服务没拉起来我发了半天消息没回应登上去一看进程根本没起。从那以后我就想能不能让服务每次启动成功就主动给我发条飞书消息这样我一眼就知道「它活了」。这个需求听起来简单但真动手会踩坑。OpenClaw 自带一个boot-md启动钩子很多人第一反应是用它来发通知结果发现它根本不是执行 shell 命令的而是把BOOT.md的内容当提示词交给 agent 去执行。更坑的是它执行完会删掉主会话映射导致只有第一次启动能发消息后面重启全哑火。如果你同时再叠一个 systemd 方案完整重启服务器时两个地方都发你会收到两条重复通知。所以这篇要讲的是最终稳定方案用 systemd 用户级服务的ExecStartPost钩子在 Gateway 启动成功后执行一条发消息命令。核心检索词就是 OpenClaw 启动自动发通知到飞书适合所有把 OpenClaw 注册成 systemd 服务、想让服务状态可视化的自托管用户。整套配置下来不到十分钟但能省掉你无数次「它到底起没起」的焦虑。先说清楚ExecStartPost的触发时机这是理解整个方案的关键。systemd 的ExecStartPost指令定义的是「主进程ExecStart启动成功之后」要执行的命令。注意这里的「成功」有明确含义对于Typesimple的服务systemd 认为ExecStart一 fork 出进程就算启动ExecStartPost紧接着就跑对于Typenotify或Typeforking则要等主进程发出就绪信号或父进程退出后才触发。OpenClaw Gateway 通常是长驻前台进程属于前者。这意味着只要服务没在启动瞬间崩溃通知就会发出来——如果启动直接失败比如配置错误、端口占用ExecStartPost不会执行你收不到消息这反而是个有用的信号没消息 没起来。理解了这一点你就能明白为什么它比boot-md靠谱ExecStartPost是 systemd 原生机制跟 OpenClaw 内部逻辑完全解耦不依赖 agent 会话状态每次启动都稳定触发不会重复也不会遗漏。下面进入实操。2. 前置准备确认 OpenClaw 已注册为 systemd 用户服务在写ExecStartPost之前你得先确认 OpenClaw Gateway 已经是一个 systemd 用户级服务。为什么强调「用户级」因为 OpenClaw 默认以当前用户身份运行服务文件放在~/.config/systemd/user/下用systemctl --user管理不需要 root 权限也不会污染系统级服务列表。先确认服务文件存在。以 root 用户为例路径是ls -l /root/.config/systemd/user/openclaw-gateway.service如果你不是 root把/root换成你的家目录比如/home/yourname/.config/systemd/user/openclaw-gateway.service。文件在说明服务已注册不在你需要先按 OpenClaw 官方文档把它注册成用户服务本文不展开注册流程。接着确认服务当前状态和它用的启动命令systemctl --user status openclaw-gateway.service systemctl --user cat openclaw-gateway.servicecat会把服务文件内容打印出来重点看[Service]段里的ExecStart那是 Gateway 的主启动命令。你要在它下面加ExecStartPost。同时留意Type如果是simple或没写默认 simple启动后立即触发 Post如果是notify则等就绪信号。还有一个前提openclaw这个 CLI 命令要能被 systemd 找到。systemd 用户服务执行命令时用的 PATH 可能跟你登录 shell 不一样所以ExecStartPost里最好写命令的绝对路径。先查一下which openclaw假设输出/usr/bin/openclaw那后面配置里就用这个绝对路径。如果which找不到说明 openclaw 装在某个虚拟环境或 npm 全局目录里你需要找到真实路径填进去否则ExecStartPost会静默失败。最后确认飞书侧的准备工作你需要一个飞书自定义机器人的 webhook 地址或者用 OpenClaw 自带的message send命令直接发给指定用户。本文主推后者因为它复用 OpenClaw 已有的飞书通道配置不用额外维护 webhook。你需要拿到目标飞书用户的 ID格式通常是ou_开头的一串字符。获取方式在飞书里给 OpenClaw 发条消息然后看 OpenClaw 的日志或会话记录里对方的open_id。这个 ID 填错的话消息会发到别人那里务必核对。注意ExecStartPost里执行的命令如果失败默认不会让服务启动失败除非你加了-前缀的特殊处理或Type相关约束但失败信息会进 journal。所以配完一定要用journalctl验证别配完就不管了。3. 可复制配置unit 片段与飞书通知脚本这一节给你可以直接抄的配置。分两部分改 systemd unit 文件以及可选写一个独立脚本让 unit 调用。先编辑服务文件nano /root/.config/systemd/user/openclaw-gateway.service找到[Service]段在末尾ExecStart之后加一行ExecStartPost。最简版本直接内联命令[Service] Typesimple ExecStart/usr/bin/openclaw gateway start ExecStartPost/usr/bin/openclaw message send --channel feishu --target user:ou_xxxxx --message OpenClaw 启动完成已在线 Restarton-failure把ou_xxxxx换成你自己的飞书用户 ID--message换成你想收到的文案。注意--target的格式是user:ou_xxxxx前缀user:不能省这是 OpenClaw 用来区分发送目标类型的。如果你觉得内联命令太长、想加日志或做条件判断可以写个独立脚本让ExecStartPost调用它。脚本放在/root/.config/systemd/user/notify-feishu.sh#!/usr/bin/env bash set -euo pipefail LOG/root/.config/systemd/user/notify-feishu.log TARGETuser:ou_xxxxx MESSAGEOpenClaw 启动完成时间$(date %Y-%m-%d %H:%M:%S) echo [$(date %F %T)] 开始发送飞书通知 $LOG if /usr/bin/openclaw message send --channel feishu --target $TARGET --message $MESSAGE $LOG 21; then echo [$(date %F %T)] 发送成功 $LOG else echo [$(date %F %T)] 发送失败退出码 $? $LOG exit 1 fi给它执行权限chmod x /root/.config/systemd/user/notify-feishu.sh然后 unit 里改成ExecStartPost/root/.config/systemd/user/notify-feishu.sh用脚本的好处是日志独立、方便调试、以后想加「启动后顺便上报 IP」之类的逻辑也好扩展。坏处是多一个文件要维护。我个人推荐脚本版因为排障时notify-feishu.log比翻 journal 直观。如果你更想用飞书自定义机器人 webhook 而不是 OpenClaw 的 message 通道脚本可以改成 curl 调用。飞书 webhook 的 JSON 结构如下{ msg_type: text, content: { text: OpenClaw 启动完成已在线 } }对应脚本片段WEBHOOKhttps://open.feishu.cn/open-apis/bot/v2/hook/你的webhook-id curl -s -X POST $WEBHOOK \ -H Content-Type: application/json \ -d {msg_type:text,content:{text:OpenClaw 启动完成已在线}}webhook 方式不依赖 OpenClaw 自身的飞书配置但需要你单独申请机器人并拿到地址。两种方式选一种即可别同时配否则又是重复通知。改完 unit 文件后必须重载 systemd 配置systemctl --user daemon-reload如果你之前试过boot-md钩子方案记得清理旧配置否则会和 systemd 方案打架rm -f /root/.openclaw/workspace/BOOT.md这一步很多人忘结果重启后收到两条消息还以为是 systemd 配错了。4. 验证请求与成功结果重启服务看通知配置写完不算完得实际验证。最直接的验证方式是重启服务然后看飞书有没有收到消息。先重启systemctl --user restart openclaw-gateway.service等几秒打开飞书看有没有收到那条通知。正常情况下服务启动成功后消息就到了。如果没收到先别急着改配置按下面顺序排查。第一步看服务状态是否真的启动成功systemctl --user status openclaw-gateway.service输出里Active:应该是active (running)。如果是failed说明主进程没起来ExecStartPost自然不会执行这时候要先去解决 Gateway 本身的启动问题。第二步看ExecStartPost的执行日志。用 journal 过滤journalctl --user -u openclaw-gateway.service -n 50 --no-pager你会看到类似这样的输出Started openclaw-gateway.service. openclaw-gateway.service: Executing ExecStartPost...如果脚本版再去翻独立日志cat /root/.config/systemd/user/notify-feishu.log日志里会明确写「发送成功」还是「发送失败退出码 X」。退出码能帮你定位127通常是命令路径不对1是 OpenClaw 命令本身报错比如 target 格式错、飞书通道没配好。第三步手动跑一遍命令排除是命令本身的问题/usr/bin/openclaw message send --channel feishu --target user:ou_xxxxx --message 手动测试手动能发出去说明命令没问题问题在 systemd 环境手动也发不出去说明是 OpenClaw 的飞书配置或 target ID 有问题。我实测下来最常见的失败原因是 PATH 和 target 格式。systemd 用户服务的 PATH 往往只有/usr/bin:/bin如果你的 openclaw 装在/usr/local/bin或 nvm 目录绝对路径写错就会 127。target 少写user:前缀OpenClaw 会报参数错误。验证成功的标志很明确每次systemctl --user restart之后飞书稳定收到一条消息不多不少。你可以连续重启三次确认for i in 1 2 3; do systemctl --user restart openclaw-gateway.service; sleep 5; done飞书应该收到三条独立通知。如果只收到一条或两条说明有触发时机问题回去检查Type和是否有其他重复配置。5. 本篇常见错误排查401、local proxy failed、OAuth 与重复通知这一节把实际会撞到的报错列出来对照着查。报错一401 Unauthorized或invalid access token这通常出现在用 webhook 方式时webhook 地址被改错或机器人被停用。如果你用的是 OpenClaw 的message send通道401 一般意味着 OpenClaw 的飞书应用凭证过期或权限被回收。去 OpenClaw 的飞书通道配置里重新确认 app id / app secret或者重新授权。注意这跟 systemd 无关是飞书侧的问题。报错二local proxy failed或连接超时这个报错说明ExecStartPost执行时网络还没就绪或者 systemd 服务的网络环境受限。用户级服务默认继承登录会话的网络一般没问题。如果你在容器或特殊网络环境里跑可能需要在 unit 里加Afternetwork-online.target和Wantsnetwork-online.target。但注意用户级服务对network-online.target的支持有限更稳的做法是在脚本里加重试for i in 1 2 3 4 5; do if /usr/bin/openclaw message send --channel feishu --target $TARGET --message $MESSAGE; then break fi sleep 3 done报错三reading choices或 JSON 解析错误这类报错通常来自 webhook 返回体解析。飞书 webhook 成功返回{code:0,...}失败返回非 0 code。如果你用 curl 但没检查返回会误以为成功。建议在脚本里加判断resp$(curl -s -X POST $WEBHOOK -H Content-Type: application/json -d $PAYLOAD) echo $resp | grep -q code:0 || { echo webhook 发送失败: $resp; exit 1; }报错四OAuth 相关错误如果 OpenClaw 的飞书通道用的是 OAuth 授权模式token 过期会导致发送失败。这类错误信息里通常带oauth或refresh token。解决办法是重新走一遍授权流程或者确认 OpenClaw 有自动刷新 token 的机制。这属于 OpenClaw 通道配置范畴不是 systemd 能解决的。问题五收到重复通知这是最典型的多配置冲突。检查三处boot-md钩子是否已删BOOT.md是否存在、unit 里是否有多条ExecStartPost、是否有其他定时任务或脚本也在发通知。用grep -r message send /root/.config/systemd/user/搜一遍把多余的删掉。问题六服务启动成功但通知没发先看ExecStartPost是否真的被执行。journal 里搜ExecStartPost如果没有这行说明 unit 文件没生效回去daemon-reload。如果有这行但脚本没输出检查脚本执行权限和 shebang。如果脚本有输出但发送失败看退出码。提示调试阶段可以在ExecStartPost命令前加-前缀如ExecStartPost-/path/script.sh这样即使命令失败也不会影响服务状态方便你反复试。稳定后再去掉。6. 把通知接进你的日常运维流配好之后这条飞书通知的价值不只是「知道它起了」。你可以把它当成一个轻量的服务健康信号机器重启、服务崩溃后自动重启、手动升级后重启每一种情况你都会收到消息。时间一长你对「什么时候该收到消息」会有直觉某天该收到却没收到反而说明有问题。如果你还想更进一步可以在脚本里加上机器 IP、当前版本号、启动耗时这些信息让通知更有信息量。比如IP$(hostname -I | awk {print $1}) VERSION$(/usr/bin/openclaw --version 2/dev/null || echo unknown) MESSAGEOpenClaw 启动完成 | IP: $IP | 版本: $VERSION | 时间: $(date %F %T)这样每条通知都是一份迷你状态报告。对于跑多个 OpenClaw 实例的人还可以在消息里带上实例名一眼区分是哪台机器。如果你在配置过程中需要查 OpenClaw 的 message 命令参数、飞书通道配置细节或者想确认 API 调用方式可以看官方文档和 API 入口。模型对话调试、Coding Plan 长期编码、API Keys 管理这些入口都在下面按需取用模型对话调试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notifyCoding Plan长期编码/Agent 场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notify控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notifyAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notify接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notifyClaude Code / Anthropic 接入https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentopenclaw_feishu_notify最后留一个我踩过的坑ExecStartPost里如果调用的是需要读取用户环境变量的命令记得在 unit 里用Environment显式声明或者脚本里source对应的 profile。systemd 用户服务不会自动加载.bashrc这点跟你在终端里手动跑命令差别很大。配完第一次重启验证通过后建议再完整重启一次机器reboot确认开机自启链路也正常因为「服务重启」和「开机自启」走的触发路径在时序上略有不同前者你已经在登录会话里后者可能网络更晚就绪。