1. OpenClaw 浏览器自动化在 systemd 下 DISPLAY 丢失的真实场景
OpenClaw 的浏览器自动化能力,本质上是让一个带图形界面的浏览器在后台被程序驱动。它适合做需要登录态、需要渲染、需要点击交互的网页任务,比如内容平台发布、后台系统操作、带验证码的流程复现。问题在于,这类任务一旦交给 systemd 托管,浏览器就很容易起不来,报错往往指向 X11 显示服务器连不上。
我遇到的现象很典型:手动在终端里执行openclaw browser start一切正常,浏览器窗口能弹出来;但同样的命令被 systemd 服务拉起时,进程存在、日志却在刷cannot open display或者Missing X server or $DISPLAY。这不是 OpenClaw 本身坏了,而是 systemd 服务进程没有继承到 DISPLAY 和 XAUTHORITY 这两个关键环境变量。
要理解这件事,得先分清两条线。第一条是「谁在跑」:OpenClaw Gateway 可能是用户级服务(systemctl --user),也可能是系统级服务(systemctl),两者的环境加载路径完全不同。第二条是「图形会话在哪」:VNC 或本地 X 会话通常跑在:1或:0,对应/tmp/.X11-unix/X1这样的 socket 文件。服务进程如果不知道 DISPLAY 指向哪个显示号,浏览器就没有落脚点。
这篇排查清单按三条线展开:服务单元配置、会话环境注入、进程继承验证。每一步都给可复制的命令和配置片段,最后把服务侧 endpoint 改到 TaoToken 后逐项复测,确保浏览器自动化和模型调用两条链路都通。
2. TaoToken 前置准备:把服务侧 endpoint 与 Key 配好
在动 systemd 之前,先把 OpenClaw 要用的模型侧配置准备好。浏览器自动化只是执行层,真正驱动决策的模型请求需要走一个稳定的 endpoint。TaoToken 在这里扮演的是统一接入层:你拿到一个 Base URL 和一个 API Key,就能在 OpenClaw 的配置里替换掉原来的模型地址,不用改业务代码。
先到控制台创建 Key。打开 https://taotoken.net/api-keys ,登录后新建一个 API Key,复制出来。这个 Key 只显示一次,建议直接写进环境变量文件而不是硬编码在服务单元里。Base URL 用https://taotoken.net/api,注意这个地址不带任何查询参数。
模型 ID 按你实际要用的填,比如claude-sonnet-4-20250514或gpt-4o这类。OpenClaw 的配置通常放在用户目录下的配置文件中,具体路径取决于你的安装方式。如果你用的是 Claude Code 风格的配置,可以打开 https://taotoken.net/doc 对照文档里的字段名。
一个常见的做法是把模型配置写进~/.openclaw/config.json或项目根目录的.env。下面是一个可复制的 JSON 片段,路径按你实际安装位置调整:
{ "model": { "provider": "openai-compatible", "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model_id": "claude-sonnet-4-20250514" }, "browser": { "headless": false, "display": ":1" } }如果你更习惯用环境变量,可以在服务单元里通过EnvironmentFile引入:
# /root/.config/openclaw/env OPENCLAW_BASE_URL=https://taotoken.net/api OPENCLAW_API_KEY=sk-你的Key OPENCLAW_MODEL_ID=claude-sonnet-4-20250514这里有个容易踩的坑:Key 里如果包含特殊字符,写进 systemd 的Environment=时要用引号包住,否则会被截断。用EnvironmentFile更省心,文件权限设成600,避免被其他用户读到。
配置好之后先别急着重启服务,用一条 curl 验证模型侧通不通:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的Key" | head -c 300返回模型列表就说明 Key 和 Base URL 没问题。这一步单独验证,能把「模型连不上」和「浏览器起不来」两类问题分开,后面排障时不会互相干扰。
3. 可复制的 systemd 单元与 DISPLAY 注入配置
现在进入核心部分。先确认 OpenClaw Gateway 到底是用户级还是系统级服务,这决定了配置文件放哪。
systemctl --user status openclaw-gateway 2>/dev/null | head -5 systemctl status openclaw-gateway 2>/dev/null | head -5如果第一条有输出且显示Loaded: /root/.config/systemd/user/openclaw-gateway.service,那就是用户级服务。用户级服务的配置目录在~/.config/systemd/user/,系统级在/etc/systemd/system/。两者不要混用,改错地方重启也不会生效。
确认显示号。VNC 通常跑在:1,本地 X 会话可能是:0:
ls -la /tmp/.X11-unix/看到X1就说明显示号是:1。同时确认 XAUTHORITY 文件位置,root 用户一般是/root/.Xauthority:
ls -la /root/.Xauthority接下来创建 drop-in 配置。drop-in 的好处是不动原始单元文件,升级时不会被覆盖。用户级服务执行:
mkdir -p /root/.config/systemd/user/openclaw-gateway.service.d然后写入/root/.config/systemd/user/openclaw-gateway.service.d/display.conf:
[Service] Environment="DISPLAY=:1" Environment="XAUTHORITY=/root/.Xauthority" EnvironmentFile=-/root/.config/openclaw/env注意EnvironmentFile前面的-表示文件不存在时不报错,适合可选配置。如果你用的是系统级服务,路径换成/etc/systemd/system/openclaw-gateway.service.d/display.conf,但系统级服务默认不继承用户会话的 X11 权限,还需要额外处理 xhost 或 XAUTHORITY 权限,所以能用户级就用户级。
重新加载并重启:
systemctl --user daemon-reload systemctl --user restart openclaw-gateway如果你同时改了模型配置,重启后服务会重新读取env文件里的 Base URL 和 Key。这里给一个完整的用户级单元示例,方便对照字段:
[Unit] Description=OpenClaw Gateway After=network.target [Service] Type=simple ExecStart=/usr/bin/openclaw gateway Restart=on-failure Environment="DISPLAY=:1" Environment="XAUTHORITY=/root/.Xauthority" EnvironmentFile=-/root/.config/openclaw/env [Install] WantedBy=default.targetWantedBy=default.target是用户级服务的写法,系统级对应multi-user.target。写错会导致systemctl --user enable失败。
还有一个隐藏问题:VNC 的 xstartup 如果没有加载 nvm,VNC 终端里的 node 版本可能和系统不一致。这会影响 OpenClaw 依赖的 Node 运行时。编辑/root/.vnc/xstartup,加上 nvm 初始化:
#!/bin/bash unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS export DISPLAY=:1 export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" if [ -x /usr/bin/dbus-launch ]; then eval $(dbus-launch --sh-syntax --exit-with-session) fi exec startxfce4改完重启 VNC:
vncserver -kill :1 vncserver :1 -geometry 1280x800 -depth 24这样 VNC 会话里的 Node 版本就和系统一致了,OpenClaw 在图形会话里手动测试时不会因为版本错位出现奇怪报错。
4. 验证请求与成功结果:从进程环境到浏览器启动
配置写完必须验证,不能只看服务状态是 active 就认为好了。按下面顺序逐项确认。
第一步,确认服务进程真的拿到了 DISPLAY。先拿 PID:
systemctl --user show openclaw-gateway -p MainPID假设输出MainPID=12345,然后读进程环境:
cat /proc/12345/environ | tr '\0' '\n' | grep -E 'DISPLAY|XAUTHORITY'期望看到:
DISPLAY=:1 XAUTHORITY=/root/.Xauthority如果这里为空,说明 drop-in 没生效,回到上一步检查路径和daemon-reload。
第二步,验证 X11 连通性。用xdpyinfo或xset测试:
DISPLAY=:1 XAUTHORITY=/root/.Xauthority xdpyinfo | head -5能打印出屏幕分辨率、深度等信息就说明 X 服务器可达。如果报unable to open display,检查 VNC 是否在跑、/tmp/.X11-unix/X1是否存在、XAUTHORITY 权限是否可读。
第三步,验证模型侧请求。用服务同样的环境变量发一条请求:
source /root/.config/openclaw/env curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $OPENCLAW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"'"$OPENCLAW_MODEL_ID"'","messages":[{"role":"user","content":"ping"}]}' \ | head -c 300返回带choices的 JSON 就说明模型链路通。这一步和浏览器无关,单独验证能排除 endpoint 配置错误。
第四步,启动浏览器自动化:
openclaw browser start openclaw browser open https://example.com如果浏览器正常启动并打开页面,说明 DISPLAY 注入成功。此时再看服务日志:
journalctl --user -u openclaw-gateway -n 50 --no-pager日志里不应该再有cannot open display或Missing X server。如果还有,重点看报错发生在哪个阶段:是浏览器二进制启动失败,还是页面加载超时。前者是 X11 问题,后者可能是网络或模型决策问题。
一个完整的成功链路应该是:systemd 服务启动 → 进程环境含 DISPLAY → 浏览器进程继承 DISPLAY → 模型请求走 TaoToken 返回 → 自动化任务执行。任何一环断了,日志都会指向具体位置。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
排障时最怕报错信息模糊。下面按真实报错逐条对照。
401 Unauthorized。模型请求返回 401,通常是 Key 没读到或写错。检查EnvironmentFile路径是否正确、文件权限是否可读、Key 是否有多余空格。用systemctl --user show openclaw-gateway -p Environment看服务实际加载了哪些变量。如果 Key 写在Environment=里且含特殊字符,改用EnvironmentFile。
local proxy failed。这个报错一般出现在 OpenClaw 尝试通过本地代理转发请求时。检查配置里是否残留了旧的代理地址,把 Base URL 统一改成https://taotoken.net/api。同时确认没有设置HTTP_PROXY之类的环境变量指向不存在的本地端口。用env | grep -i proxy排查。
reading choices 报错。这通常意味着模型返回的 JSON 结构不符合预期,常见原因是 Base URL 少了/v1或多了斜杠。TaoToken 的 API 地址是https://taotoken.net/api,具体路径按文档拼接。用 curl 直接请求一次,看返回体里有没有choices字段。如果返回的是 HTML 错误页,说明地址写错了。
OAuth 相关报错。如果你用的是 Claude Code 或 Codex 风格的认证,OAuth token 过期会报认证失败。这类场景建议改用 API Key 方式,在 https://taotoken.net/api-keys 重新生成一个,写进env文件。OAuth 和 API Key 不要混用,混用会导致认证头冲突。
DISPLAY 设置了但浏览器仍起不来。检查 XAUTHORITY 权限。ls -la /root/.Xauthority应该是600且属主是运行服务的用户。如果服务以其他用户运行,需要把 XAUTHORITY 复制过去或调整权限。另外确认 VNC 的-depth参数,某些浏览器需要 24 位色深。
服务重启后配置丢失。drop-in 文件如果放在错误目录,daemon-reload不会加载。用户级服务必须在~/.config/systemd/user/下,系统级在/etc/systemd/system/下。用systemctl --user cat openclaw-gateway查看实际生效的配置。
Node 版本不一致。VNC 终端里node -v和系统不一致,说明 xstartup 没加载 nvm。按第 3 节的 xstartup 片段修改,重启 VNC 后验证。OpenClaw 依赖的 Node 版本如果低于要求,会在启动时报语法错误。
排查时养成一个习惯:每改一处配置,只重启对应服务,然后立刻验证。不要一次改多个地方,否则出问题不知道是哪一步引入的。
6. 把服务侧 endpoint 改到 TaoToken 后的复测清单
配置改完后,按这个清单逐项复测,确保浏览器自动化和模型调用都稳定。
先确认服务状态和进程环境:
systemctl --user status openclaw-gateway --no-pager systemctl --user show openclaw-gateway -p MainPID cat /proc/$(systemctl --user show openclaw-gateway -p MainPID --value)/environ | tr '\0' '\n' | grep -E 'DISPLAY|OPENCLAW'再验证 X11 和模型两条链路:
DISPLAY=:1 XAUTHORITY=/root/.Xauthority xdpyinfo | head -3 source /root/.config/openclaw/env curl -s https://taotoken.net/api/v1/models -H "Authorization: Bearer $OPENCLAW_API_KEY" | head -c 200最后跑一次完整的浏览器自动化任务,观察日志:
openclaw browser start openclaw browser open https://example.com journalctl --user -u openclaw-gateway -n 30 --no-pager如果日志干净、浏览器正常打开、模型请求返回正常,说明整条链路通了。后续如果要做长期编码或 Agent 任务,可以在 https://taotoken.net/coding-plan 看套餐配置;需要调试模型对话效果,用 https://taotoken.net/models 直接测试;接入文档在 https://taotoken.net/doc 对照字段。Key 管理统一在 https://taotoken.net/api-keys 。
一个实用技巧:把复测命令写成一个脚本,每次改配置后跑一遍,比手动敲命令快得多。脚本里用set -e,任何一步失败就停,避免带着问题往下走。