Audience: teams running Gateway and openclaw CLI in separate Compose services, seeing Gateway logs OK while CLI hits gateway closed (WebSocket 1006/1008), pairing required, or twin token tracks (env versus file). This page argues: fix the gateway URL semantics (service name TCP or shared Unix socket), collapse OPENCLAW_GATEWAY_TOKEN and gateway.auth.* into a single source of truth, then—only if justified—narrow the calling surface via shared network namespaces. Scope versus April’s sub-agent 1008 + trustedProxies article: that one optimizes trusted CIDR paths; here we focus how each container literally addresses the Gateway plus matching tokens—cross-read with Docker network checklist, pairing & tokens, and volume permissions.
127.0.0.1: loopback names the current container only; use Compose DNS such as http://openclaw-gateway:18789 (adapt service names).OPENCLAW_GATEWAY_TOKEN may override filesystem config; mismatched mounts look like intermittent 401/1008 flips.gateway.sock, both sides need the identical host path mapped with aligned UID/GID (see volumes guide).network_mode: service:<gateway> or consistently stick to DNS TCP URLs—never A/B hybrid env blobs in one change ticket.Treat the five bullets as release-blocker checkboxes until each is green or explicitly waived—changing image tags first rarely fixes unresolved namespace/token contracts.
Add a timestamp triad when WebSocket closes: align Gateway stderr, CLI stdout, and docker events to the millisecond—“random” outages often correlate with restarts or aggressive health checks.
| Symptom | Investigate next (top-down) | Usually lands here |
|---|---|---|
1006 plus fresh Gateway PID / OOM | same-window host/kernel logs → restart budgets → timeouts | transport closed ≠ bad pairing policy |
1008 plus pairing required | token truth → Gateway URL env → persisted pairing bits on disk | dual mounts or unreadable state dirs |
Connection refused inside CLI container only | move probes into that container; host curls are anecdotes | loopback misconception or unpublished port mapping |
| Unix socket ENOENT / permission denied | ls -la mounts; confirm parent dirs share the bind | socket missing from child namespace path |
Closure codes cannot stand alone: 1006 and 1008 are labels—you must correlate them with Gateway logs, pairing state machines, and any reverse-proxy retry budgets; otherwise widening bind or blindly re-onboarding only grows exposure.
docker compose config plus CI grep hooks..openclaw state identically: Gateway and CLI must read the same writable tree; pairing appears “stuck on step zero” when state is RO—walk volume checklist.openclaw gateway status, then openclaw doctor; align timestamps between host daemon logs and inner-container logs.network_mode: service:<gateway> OR mesh-side alternatives plus firewall rows consistent with trustedProxies playbooks.If an outer Nginx/Caddy fronts the Gateway, reconcile Upgrade/idle timeouts between reverse-proxy guidance and the Compose-facing URL—or intermittent 1006 becomes indistinguishable from auth bugs.
# Placeholder illustration—replace names/paths and run docker compose config
services:
openclaw-gateway:
environment:
- OPENCLAW_CONFIG_DIR=/data/.openclaw
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
volumes:
- oc-data:/data/.openclaw
networks: [oc-net]
openclaw-cli:
environment:
- OPENCLAW_CONFIG_DIR=/data/.openclaw
- OPENCLAW_GATEWAY_URL=http://openclaw-gateway:18789
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
volumes:
- oc-data:/data/.openclaw
networks: [oc-net]
depends_on:
- openclaw-gateway
networks:
oc-net:
driver: bridge
volumes:
oc-data:
Demos are fine on a single host; production automation plus rotating secrets needs predictable restarts, disk baselines, and audit-friendly pairing history. Parking the Gateway on a dedicated remote Mac with known Six-region coverage and monthly/quarterly rental math usually pairs better with CI secret cadence than personal machines that sleep: MACCOME Apple Silicon nodes keep “Gateway always on” and “human pairing windows” on one operational contract. Start at the public Mac mini rental rates page and help center, then map env vars from this runbook.
Almost every Compose failure here is namespace + token + state directory written as three different documents. Until you show a repeatable in-container handshake capture, do not “swap images” or “disable TLS”; when code vs infra debates flare, elevate whichever side can replay the successful websocket inside the container—everything else waits in queue.
After contracts hold, integrate with GHCR + Control UI flows and hardened reverse proxies.
FAQ
Overlap with April’s trustedProxies pairing article?
That playbook covers coarse CIDRs and sub-agent traffic; here we wire URLs, sockets, namespaces, and single-source tokens—read both, discard neither.
If host curl succeeds, is CI necessarily fine?
No. Re-run probes inside the same container image that failed; mismatched namespaces make host curls noise.