- Install highlight.js via npm with 8 language definitions - SourceViewer hook applies syntax highlighting on mount/update - Atom One Dark theme for dark mode, Atom One Light for light mode - Add hooks directory, package.json, and package-lock.json - Add demo document and screenshots - Add rodney-docker.md documentation - Ignore .rodney/ chrome data directory
3.7 KiB
3.7 KiB
Rodney + Docker Headless Chrome
When Chrome/Chromium can't run directly in the host environment (permission issues, snap confinement, missing capabilities), use a Docker container as a minimal Chrome host for Rodney.
Why Docker?
Rodney needs a Chrome DevTools Protocol (CDP) endpoint. Chrome requires:
CAP_SYS_ADMIN— for sandbox namespace creation (even with--no-sandbox, Chrome internally checks for this)- Read/execute access to Chrome binary — often blocked when the binary is root-owned and the process has no
CAP_DAC_OVERRIDE - Writable temp directory — for user data/profile
In restricted environments (e.g. pi agent with zero capabilities, snap confinement), these are hard to grant minimally on the host. Docker isolates the Chrome process with just the one capability it needs.
Build
# Copy the Chromium binary that `rodney start` downloads:
cp -r ~/.cache/rod/browser/chromium-*/ .pi/skills/demo/chrome/
# Build the image:
docker build -t demo-chrome .pi/skills/demo/
The Dockerfile is at .pi/skills/demo/Dockerfile. It uses:
debian:bookworm-slimas base (minimal)- Pre-downloaded Chromium from rod's cache (
~/.cache/rod/browser/chromium-*/) - Only the shared libraries Chrome actually needs (no snap, no desktop deps)
Run
docker run -d --name demo-chrome \
--cap-add=SYS_ADMIN --cap-drop=ALL \
--security-opt=no-new-privileges:false \
--network=host \
demo-chrome
Flag breakdown
| Flag | Why |
|---|---|
--cap-add=SYS_ADMIN --cap-drop=ALL |
Drop all capabilities, add only what Chrome needs. Blast radius: one capability in one container. |
--security-opt=no-new-privileges:false |
Allows the setuid chrome-sandbox to escalate. Required for Chrome's sandbox helper. |
--network=host |
Critical — Chrome binds to 127.0.0.1 inside the container. Docker port mapping (-p) doesn't work reliably with Chrome's localhost binding. --network=host makes Chrome bind directly to the host's network namespace. |
Connect
rodney connect localhost:9222
Chrome exposes the CDP endpoint on port 9222. Rodney connects via WebSocket.
Verify
# Check Chrome is responding:
curl -s http://localhost:9222/json/version
# Take a screenshot:
rodney open http://localhost:8056/microprints
rodney waitidle
rodney screenshot demos/screenshots/microprints.png
Stop
docker stop demo-chrome && docker rm demo-chrome
Troubleshooting
Chrome not responding on port 9222
- Check logs:
docker logs demo-chrome - Look for
"DevTools listening on ws://..."— if present, Chrome is running - If you see
libXfixes.so.3: cannot open shared object, the Dockerfile needslibxfixes3in the apt install
Connection reset by peer
- Chrome is binding to
127.0.0.1inside the container. Docker port mapping (-p 9222:9222) doesn't forward correctly. - Fix: Use
--network=hostinstead of-p.
Permission denied on /opt/google/chrome/chrome
- The process has zero capabilities (
CapEff: 0000000000000000). Can't execute root-owned binaries. - Fix: Docker container with
--cap-add=SYS_ADMINbypasses this.
dconf errors
- Chrome tries to write to
/run/user/$UID/dconfwhich may be read-only or permission-restricted. - Fix: Docker container has its own writable filesystem.
dbus errors (harmless)
Failed to connect to the bus— Chrome tries to connect to system dbus which doesn't exist in the container.- Ignore — these are non-fatal warnings. Chrome works fine without dbus.
GLib-GIO-CRITICAL errors (harmless)
g_settings_schema_source_lookup: assertion 'source != NULL' failed— Chrome tries to read GTK settings which don't exist.- Ignore — non-fatal warnings in headless mode.