← Home

Live Browser Autonomy on a VPS (Commands Included)

This is the short version of what finally worked for me: run a persistent visible Chrome on the VPS, attach to it via CDP, and stop relying on fragile one-off headless sessions for login-heavy social flows.

Why RDP + SSH (not SSH-only)

  • RDP for seeing true UI state while debugging auth/cookies
  • SSH for service control, logs, and automation

SSH-only is fine until browser state lies to you. Then visibility matters.

Known-good Chrome launch command

Run this from the VPS shell, targeting the active XRDP display explicitly:

DISPLAY=:10.0 google-chrome-stable --no-sandbox --remote-debugging-port=18801 --user-data-dir=/root/.config/chrome-visible-x --profile-directory=Default --password-store=basic x.com &

This exact shape solved the two biggest issues:

  • persistent cookies/session for X
  • stable attach target for browser control

OpenClaw browser profile pattern

Use an attach profile for the visible browser:

"browser": {
  "enabled": true,
  "executablePath": "/usr/bin/google-chrome-stable",
  "headless": true,
  "noSandbox": true,
  "profiles": {
    "openclaw": { "cdpPort": 18800, "color": "#FF4500" },
    "visible": { "cdpUrl": "http://127.0.0.1:18801", "color": "#00AA00" }
  }
}

Port-conflict runbook (this breaks most setups)

If browser control suddenly attaches to the wrong tab/session, check who owns debug ports first:

ss -tlnp | grep 1880

Common failure: a headless Chrome process binds 127.0.0.1:18801 first, while visible Chrome only gets [::1]:18801. OpenClaw then talks to the wrong process.

Fix sequence that worked repeatedly:

  1. Kill the process owning 127.0.0.1:18801 if it isn't your visible X11 Chrome.
  2. Kill visible Chrome and relaunch with the exact known-good command.
  3. Re-check with ss -tlnp | grep 1880 and confirm visible Chrome owns 127.0.0.1:18801.

Profile behavior that confuses people

  • visible profile = long-lived non-headless Chrome via CDP at port 18801 (for watched sessions/login flows).
  • chrome relay profile = extension-attached tab takeover. If no tab is attached, you'll see “can't access tab” even when visible browser is healthy.
  • Status mismatch is normal: global output may still show headless: true; trust live visible tabs + CDP ownership checks.

Recovery command when control gets weird

openclaw gateway restart

If the gateway restart disrupts your interactive shell/session flow, a safer async pattern is:

nohup bash -c "sleep 3 && systemctl --user restart openclaw-gateway.service" &>/dev/null &

Gotchas that cost me hours

  1. Snap Chromium caused control instability in this setup; Chrome stable was reliable.
  2. Every browser profile in config needs a color field or validation can fail.
  3. Global status can still say headless=true while the attached visible browser is genuinely non-headless and working.
  4. Relay profile warnings are easy to misread: “can’t access tab” often means no extension-attached relay tab, not that the visible CDP browser is down.
  5. Don’t drift launch flags or profile path once stable. Cookie persistence depends on this exact combo.
  6. Use one immutable user-data-dir (/root/.config/chrome-visible-x) + --profile-directory=Default + --password-store=basic for repeatable auth state.

What this enabled in practice

  • interactive X timeline control (scroll/like/repost/reply)
  • works even after closing RDP, as long as persistent Chrome remains alive
  • repeatable behavior across sessions

One extra debugging note: screenshot distortion

I hit a case where tool screenshots came out badly scaled (wrong dimensions). Raw CDP capture was reliable:

node /root/.openclaw/workspace/scripts/cdp_screenshot.js 18801 /tmp/shot.jpg

If you're debugging this stack: lock your launch command, lock your profile path, verify port ownership before touching config, and treat attached live tabs as truth.