2026 OpenClaw Docker Compose Multi-Container: Gateway Binding, Token & “Subagent Pairing 1008” Network Fix Checklist (trustedProxies)

About 14 min read · MACCOME

Who hits this: You run Docker Compose with Gateway plus CLI or agent-related containers; Gateway logs look healthy, but sub-session tools throw gateway closed (1008): pairing required, or RPC is healthy yet pairing still fails. Takeaway: This is rarely fixed by “reinstalling the image again”—it is usually a combination of bind surface, token, trusted proxy CIDRs (trustedProxies), and inter-container URLs; this article provides a symptom matrix, Compose snippet, six-step runbook, and three KPIs. How it fits: alongside the official docker-setup.sh + GHCR, pairing and token, and Docker networking triage articles.

Why does Gateway look “up” in Compose while subagents still report 1008?

OpenClaw Gateway owns long-lived connections and routing; when the CLI or sub-session tools talk to Gateway over WebSocket/HTTP, the process being up is not enough—the caller must be a trusted network identity from Gateway’s perspective, and pairing must still be valid. Docker puts each service in its own network namespace: if you set http://127.0.0.1:18789 inside the CLI container, traffic hits that container, not the host or another service.

  1. localhost not replaced with a service name: use a Compose DNS name such as http://openclaw-gateway:18789 (example).
  2. bind surface vs. access path mismatch: if Gateway listens only on loopback, other containers reaching it via bridge IPs may be treated as “not local,” triggering auth or pairing branches.
  3. Missing trustedProxies: when Gateway sits behind a reverse proxy or bridge, declare Docker ranges (often 172.16.0.0/12, 10.0.0.0/8, and your custom network CIDR) so client identity is evaluated correctly.
  4. OPENCLAW_GATEWAY_TOKEN drift: Gateway and CLI read tokens from different mount paths or env—symptoms oscillate between “sometimes works” and 401/1008.
  5. Pairing state lost after upgrade: incomplete volume mounts; subagent sessions need re-pairing but you assume it is “only networking.”
  6. Ignoring recurring Docker patterns from upstream discussions: loopback bind + split containers means you must change bind or trusted-proxy policy explicitly—a network boundary contract, not a random defect label.

Unlike the three-OS install overview, Compose is less about “which OS” and more about which network is trusted inside which container.

When you reproduce the issue, keep three log fingerprints side by side: Gateway startup lines, subagent stack traces, and the attachable network Subnet from docker inspect—many 1008 cases collapse to “trusted range not declared,” not the model or channel.

Table 1: Symptom snapshot → checks (decision matrix)

Symptom snapshotCheck first (ordered)Typical root cause
Container A cannot reach 127.0.0.1:18789Switch to service-name URL, then published ports and listen addressWrong localhost semantics
RPC healthy but sessions_spawn returns 1008Pairing list → trustedProxies → bind triadSubagent path treated as external / not paired
Logs mention trusted proxy / pairingAlign compose network CIDR with gateway.auth configSubnet missing from trusted set
Only after a version upgradeDiff old vs. new default bind/auth; compare migrated .openclaw volumeTightened defaults or stale pairing
info

Note: If you use the official docker-setup.sh flow, still validate service names and volumes through this network-contract lens—see the GHCR and Control UI Runbook.

Table 2: bind mode vs. inter-container access

gateway.bind (concept)Best forCompose friction
loopbackSingle all-in-one container or host-only accessOther service containers cannot treat it as local loopback
lan / custom listenMultiple services and cross-container RPCRequires token/auth and a tightened exposure story
trusted reverse proxyNginx/Caddy in frontMust match the reverse-proxy checklist for downstream sources

Table 3: How to fill trustedProxies (example CIDRs)

The table below is illustrative: always read your real subnet from docker network inspect, do not copy blindly.

Network typeExample CIDRWhat you do
Default Docker bridge172.17.0.0/16 (environment-dependent)Check whether Gateway sees source IPs inside that range
Compose custom networkfor example 172.18.0.0/16172.30.x.xAdd the whole subnet to trustedProxies (not single container IPs)
overlay / swarmallocated by orchestratorSame principle: target subnets, not volatile pod IPs

Six-step runbook: from “containers can ping each other” to “subagents can pair”

  1. Unify OPENCLAW_CONFIG_DIR: Gateway and CLI/agents mount the same path or synchronized content so nothing reads A on one side and B on the other.
  2. Write the Gateway URL with a service name: in the CLI container env, e.g. OPENCLAW_GATEWAY_URL=http://openclaw-gateway:18789 (match your compose service name).
  3. Align the token: OPENCLAW_GATEWAY_TOKEN matches on both sides; if you use a file token, confirm the mount is readable by the container UID.
  4. Set Gateway bind and trustedProxies: after updating per Tables 2/3, restart Gateway (bind changes usually need a hard restart).
  5. Run the diagnostics chain: openclaw gateway statusopenclaw doctor → reproduce the subagent action and grep logs for 1008 / pairing.
  6. Record three KPIs: time-to-successful-pair, restart count, and whether cross-container RPC still times out—put them in the change ticket.
yaml
# Snippet example (service names and env placeholders — replace before production)
services:
  openclaw-gateway:
    environment:
      - OPENCLAW_CONFIG_DIR=/config
      - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
    volumes:
      - ./config:/config
    networks: [oc-net]

  openclaw-cli:
    environment:
      - OPENCLAW_CONFIG_DIR=/config
      - OPENCLAW_GATEWAY_URL=http://openclaw-gateway:18789
      - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
    volumes:
      - ./config:/config
    networks: [oc-net]

networks:
  oc-net: { driver: bridge }

Three KPIs worth putting on a dashboard (ops can verify)

  1. Subagent success rate: successful sessions_spawn (or equivalent) per total calls in a window; below threshold, open a “pairing + CIDR” triage page automatically.
  2. Configuration drift alert: compare gateway.auth.token fingerprint or env hash between containers; block release if they diverge.
  3. Gateway restart cost: count how many restarts bind/trustedProxies changes require; drive “config as code” and review templates.

These KPIs are engineering observability suggestions; they are not an upstream SLA commitment.

Why ad-hoc container setups struggle as an OpenClaw production backbone

“Image comes up” is not enough: subagents and session tools need a stable Gateway, predictable network identity, and consistent config volumes. Iterating with single-container docker run on a laptop is a different failure surface than a fixed Compose stack with persistent volumes on a dedicated remote Mac—the latter matches unsupervised and scheduled jobs much more closely.

Ephemeral VPS or shared desktops may demo quickly yet often lack a consistent disk and network baseline; when you need Gateway, reverse proxy, and automation in one trust zone with room to scale, multi-region Apple Silicon cloud hosts with monthly or quarterly terms are often easier than repeatedly hand-building temporary Docker hosts. MACCOME offers physical isolation and six-region placement so you can split “always-on Gateway” from “build/signing” tiers; review rental rates and the cloud Mac support and help center, then wire env vars using this runbook.

Rollout order: close the six-step loop inside one Compose project first, then decide whether Gateway and other workloads should move to separate remote nodes.

FAQ

When code 1008 appears, should I immediately change bind to LAN?

Work through Table 1 first: often it is service-name URL or trustedProxies, not blindly widening the listen surface. If you expand exposure, align token and firewall together. More pairing scenarios: pairing and token checklist.

Can I curl 127.0.0.1:18789 on the host instead of probing from the container?

That is a valid host-side health check, but it does not replace verifying container-to-Gateway connectivity via the service name—the namespaces differ. For help: cloud Mac support and help center.

Is this redundant with the Docker networking triage article?

Networking triage covers compose namespaces and CLI reachability; the official Docker article focuses on image paths. This runbook specifically bridges subagent pairing with trustedProxies. You can also read MCP and Skills verification next.