feat: run BrowserOS inside OpenClaw container#655
feat: run BrowserOS inside OpenClaw container#655shivammittal274 wants to merge 2 commits intofeat/podman-migrationfrom
Conversation
- Custom container image (Dockerfile + entrypoint) that pre-installs: - BrowserOS ARM64 .deb (headless browser with custom CDP) - BrowserOS server binary (MCP server, 60 browser tools) - browseros-cli (CLI for browser automation) - browseros-agent skill (OpenClaw skill for browser control) - Entrypoint automatically launches: 1. BrowserOS headless (CDP on port 9000) 2. BrowserOS server (MCP on port 9100, connects to CDP) 3. Configures browseros-cli 4. Starts OpenClaw gateway - agents.ts changes: - Image: ghcr.io/browseros/openclaw-runtime:latest - Port range 4000-4099 for apps OpenClaw builds - shm_size 256mb for headless browser - Health check start_period 60s - Auto-build image locally if not found in registry - Health check timeout increased to 90s Known issue: BrowserOS built-in server has a bug where it doesn't read --remote-debugging-port, so the server must run separately. This matches the pattern used by BrowserOSAppManager and test helpers. Tested e2e: create agent → OpenClaw browses Amazon → extracts products
Greptile SummaryPR #655 adds a custom Confidence Score: 4/5Three P1 issues should be resolved before merging: silent container failures in entrypoint.sh and an ARM64-only Dockerfile that breaks local builds on AMD64. Score reduced from 5 due to two P1 logic bugs in entrypoint.sh that produce zombie-healthy containers, and one P1 Dockerfile issue that breaks the auto-build fallback on non-ARM64 hosts. entrypoint.sh (both wait loops need exit-on-failure guards) and Dockerfile (arch detection for .deb URL) Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant AgentsRoute
participant Podman
participant Container
participant BrowserOS
participant BrowserOSServer
participant OpenClaw
Client->>AgentsRoute: POST /agents/create
AgentsRoute-->>Client: 201 {id, status: creating}
AgentsRoute->>Podman: ensureReady()
AgentsRoute->>Podman: image exists? / pull / build
AgentsRoute->>Podman: compose up -d
Podman->>Container: start entrypoint.sh
Container->>BrowserOS: launch --headless --disable-browseros-server
Container->>Container: wait loop (30s) for CDP:9000
Note over Container: ⚠️ No exit on timeout
Container->>BrowserOSServer: launch browseros_server
Container->>Container: wait loop (30s) for :9100/health
Note over Container: ⚠️ No exit on timeout
Container->>OpenClaw: exec node dist/index.js gateway
AgentsRoute->>AgentsRoute: poll /healthz (90 retries)
OpenClaw-->>AgentsRoute: 200 OK
AgentsRoute->>AgentsRoute: updateStatus('running')
|
| echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..." | ||
| for i in $(seq 1 30); do | ||
| if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then | ||
| echo "[entrypoint] CDP ready" | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done |
There was a problem hiding this comment.
Silent failure if CDP never becomes ready
The wait loop exits after 30 iterations without signaling failure. If BrowserOS never starts, browseros_server still launches and the container reports healthy with broken browser automation.
Add exit 1 after the loop:
| echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..." | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then | |
| echo "[entrypoint] CDP ready" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..." | |
| READY=0 | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then | |
| echo "[entrypoint] CDP ready" | |
| READY=1 | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$READY" -eq 0 ]; then | |
| echo "[entrypoint] ERROR: BrowserOS CDP did not become ready in time" >&2 | |
| exit 1 | |
| fi |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/entrypoint.sh
Line: 31-38
Comment:
**Silent failure if CDP never becomes ready**
The wait loop exits after 30 iterations without signaling failure. If BrowserOS never starts, `browseros_server` still launches and the container reports healthy with broken browser automation.
Add `exit 1` after the loop:
```suggestion
echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..."
READY=0
for i in $(seq 1 30); do
if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then
echo "[entrypoint] CDP ready"
READY=1
break
fi
sleep 1
done
if [ "$READY" -eq 0 ]; then
echo "[entrypoint] ERROR: BrowserOS CDP did not become ready in time" >&2
exit 1
fi
```
How can I resolve this? If you propose a fix, please make it concise.| echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..." | ||
| for i in $(seq 1 30); do | ||
| if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then | ||
| echo "[entrypoint] Server ready" | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done |
There was a problem hiding this comment.
Server wait loop also swallows failure silently
Same issue as the CDP loop: if browseros_server never becomes healthy, the script silently continues and starts the OpenClaw gateway, producing a healthy-looking container with broken automation.
| echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..." | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then | |
| echo "[entrypoint] Server ready" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..." | |
| READY=0 | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then | |
| echo "[entrypoint] Server ready" | |
| READY=1 | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$READY" -eq 0 ]; then | |
| echo "[entrypoint] ERROR: BrowserOS server did not become ready in time" >&2 | |
| exit 1 | |
| fi |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/entrypoint.sh
Line: 50-57
Comment:
**Server wait loop also swallows failure silently**
Same issue as the CDP loop: if `browseros_server` never becomes healthy, the script silently continues and starts the OpenClaw gateway, producing a healthy-looking container with broken automation.
```suggestion
echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..."
READY=0
for i in $(seq 1 30); do
if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then
echo "[entrypoint] Server ready"
READY=1
break
fi
sleep 1
done
if [ "$READY" -eq 0 ]; then
echo "[entrypoint] ERROR: BrowserOS server did not become ready in time" >&2
exit 1
fi
```
How can I resolve this? If you propose a fix, please make it concise.| ARG BROWSEROS_DEB_URL=http://cdn.browseros.com/releases/0.44.0.2/linux/BrowserOS_v0.44.0.2_arm64.deb | ||
| RUN apt-get update -qq && \ | ||
| curl -fsSL -o /tmp/BrowserOS.deb "$BROWSEROS_DEB_URL" && \ | ||
| apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \ |
There was a problem hiding this comment.
Hardcoded ARM64
.deb — local builds on AMD64 will install the wrong binary
The PR adds an auto-local-build fallback when the registry image is missing, so this Dockerfile will run on user machines of any architecture. The default BROWSEROS_DEB_URL points to the arm64 package, causing dpkg to fail or install an unusable binary on AMD64 hosts. Detect arch at build time instead:
| ARG BROWSEROS_DEB_URL=http://cdn.browseros.com/releases/0.44.0.2/linux/BrowserOS_v0.44.0.2_arm64.deb | |
| RUN apt-get update -qq && \ | |
| curl -fsSL -o /tmp/BrowserOS.deb "$BROWSEROS_DEB_URL" && \ | |
| apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \ | |
| ARG BROWSEROS_VERSION=0.44.0.2 | |
| RUN ARCH=$(dpkg --print-architecture) && \ | |
| curl -fsSL -o /tmp/BrowserOS.deb \ | |
| "http://cdn.browseros.com/releases/${BROWSEROS_VERSION}/linux/BrowserOS_v${BROWSEROS_VERSION}_${ARCH}.deb" && \ | |
| apt-get update -qq && \ | |
| apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \ | |
| rm /tmp/BrowserOS.deb && \ | |
| npm install -g browseros-cli && \ | |
| rm -rf /var/lib/apt/lists/* |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/Dockerfile
Line: 6-9
Comment:
**Hardcoded ARM64 `.deb` — local builds on AMD64 will install the wrong binary**
The PR adds an auto-local-build fallback when the registry image is missing, so this Dockerfile will run on user machines of any architecture. The default `BROWSEROS_DEB_URL` points to the `arm64` package, causing `dpkg` to fail or install an unusable binary on AMD64 hosts. Detect arch at build time instead:
```suggestion
ARG BROWSEROS_VERSION=0.44.0.2
RUN ARCH=$(dpkg --print-architecture) && \
curl -fsSL -o /tmp/BrowserOS.deb \
"http://cdn.browseros.com/releases/${BROWSEROS_VERSION}/linux/BrowserOS_v${BROWSEROS_VERSION}_${ARCH}.deb" && \
apt-get update -qq && \
apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \
rm /tmp/BrowserOS.deb && \
npm install -g browseros-cli && \
rm -rf /var/lib/apt/lists/*
```
How can I resolve this? If you propose a fix, please make it concise.
Summary
ghcr.io/browseros/openclaw-runtime) that runs BrowserOS headless + BrowserOS server + OpenClaw togetherbrowseros-clito control BrowserOS inside the same containerWhat's in the container
Changes
agents.tsghcr.io/browseros/openclaw-runtime:latest4000-4099mapped for apps OpenClaw buildsshm_size: 256mbfor headless browserstart_period: 60sfor container health checkopenclaw-container/Dockerfileghcr.io/openclaw/openclaw:latest.debbrowseros_serverbinary (needed because built-in server has a CDP port detection bug in headless mode)browseros-cligloballybrowseros-agentskillopenclaw-container/entrypoint.sh--disable-browseros-server(server runs separately)browseros_serverwith correct CDP/server portsbrowseros-cliKnown issue
BrowserOS built-in server (
--browseros-mcp-port) doesn't read--remote-debugging-port— it always tries CDP on the wrong port. This is why we runbrowseros_serverseparately. Same pattern used byBrowserOSAppManagerand test helpers. TODO comment exists inbrowser.ts:86.Tested e2e
POST /agents/create→ container starts with BrowserOS + server + CLIPrerequisites
browseros_serverbinary must be built and placed inopenclaw-container/dir before building the imagepodman build -t ghcr.io/browseros/openclaw-runtime:latest apps/server/src/api/services/openclaw-container/🤖 Generated with Claude Code