mem_limit · first-run WASM wait · allowedOrigins · compose exec pairing · copy-paste compose
Developers who already brought up docker compose from the guide on small-memory VPSes most often see three symptoms: containers exiting immediately with 137, port 18789 appearing hung for a long time, and Control UI errors about non-loopback or Host headers. This article starts with an actionable checklist for free -h versus mem_limit and a 3–7 minute first-run WASM compile window, then uses a symptom → docker logs evidence → fix table for Exit 137, volume permissions, and mount mismatches; next it narrows UI errors with a 127.0.0.1 binding, allowedOrigins, and reverse proxy decision table; finally it gives a six-step in-container device pairing runbook and ten pre-launch checks. Read alongside the OpenClaw v2026.4 install and Docker hardening guide, the Gateway install and doctor troubleshooting checklist, and the three-part runtime troubleshooting article to move from “the container starts” to “stable without babysitting.”
When the OpenClaw Gateway starts in a container for the first time, CPU pegged while the port stays closed is often the WASM sandbox compile phase, not a deadlock. Align with the resource guidance in the v2026.4 install guide: when both host free memory and mem_limit are too tight, Linux OOM yields Exit 137 and logs may not print a friendly error in time.
Host: run free -h on the host and confirm headroom is not chronically below about 1.5–2 GiB; Exit 137 is more common when swap is 0.
Compose: set mem_limit: 2g or higher and avoid competing with other heavy services on the same machine.
First-run window: allow 3–7 minutes on a cold start before calling it failed; use docker logs -f to see whether the compile chain is still running.
Health checks: set healthcheck.start_period to at least 360s so compose does not kill the process before WASM finishes.
Avoid false positives: do not repeatedly docker compose restart inside the first-run window or each restart retriggers the cold compile spike.
Put these five lines on page one of your “OpenClaw on a VPS” runbook to cut tickets that say “I followed the tutorial but it still won’t start.” For fuller Gateway layering, see the three-part runtime guide.
The table below keeps an “evidence first, then action” order so tickets move from “the gateway feels broken” to reproducible fields. Used with the doctor install checklist, you can paste row numbers into ticket templates.
| Symptom | docker logs / host evidence | Fix first |
|---|---|---|
| Immediate exit 137 | dmesg shows OOM; or logs stop abruptly | Raise mem_limit / upgrade size; reduce contention |
| Permission denied reading workspace | Mount owned by root; node user in the container cannot write | chown to the container UID or align user: with the volume |
| Config changes do not apply | Host path and compose volume point at two different .openclaw trees | Unify on one bind path; run docker compose config before restart |
| Intermittent DNS failure | curl to model endpoints times out in the container | Check Docker dns and host resolution per the install guide |
| Restart loop with no new logs | Health check too aggressive kills the process | Widen start_period and retries |
Efficient Docker triage hinges on proving 137 is OOM before guessing it is an application bug.
If you pair the Gateway with an always-on remote Mac node, treat “container resources” and “node SLA” as two acceptance lines: the former is compose and logs; the latter is provider region and maintenance windows.
Publishing 18789 to the public internet on a VPS is a common misconfiguration. The install guide recommends binding the container to loopback only and terminating TLS with Caddy or Nginx on 443. When Control UI reports non-loopback errors, the root cause is usually a mismatch between browser origin and what the Gateway allows, not “Docker is broken.”
| Scenario | Listen and proxy | Configuration bias |
|---|---|---|
| Local SSH tunnel debugging only | 127.0.0.1:18789 | allowedOrigins includes http://127.0.0.1:18789 |
| Production HTTPS by domain | Reverse proxy to loopback upstream | allowedOrigins lists https://your-domain.example; avoid Host fallback when you can |
| Temporary lab | HTTP on a private IP | List IP origins explicitly; shrink CIDR; retire after the maintenance window |
| Triage “just show me the UI” | Still prefer an outer TLS layer | Use official Host-header fallback switches only with understood risk; roll back afterward |
{
"gateway": {
"mode": "local",
"controlUi": {
"allowedOrigins": ["https://openclaw.example.com"]
}
}
}
Note: after editing openclaw.json, check whether docker compose restart lands inside the first-run WASM window so it does not stack with the false positives in section 2.
When you run openclaw inside the container, HOME and the config volume must match the running Gateway process or you get “approved on the host, still pending in the container.” The steps below assume the service name openclaw; replace it with your compose service name.
Confirm service name: docker compose ps and pick the container that actually runs the Gateway.
Enter the same environment: docker compose exec openclaw sh -lc 'pwd; echo $HOME; ls -la ~/.openclaw | head'
List pending pairing: docker compose exec openclaw openclaw devices list
Copy Request ID: from the UI or logs, then run openclaw devices approve <id>.
Check API key visibility: if you see No API key found for provider, verify .env is passed into the container by compose, not only in a host shell profile.
Write back ticket fields: paste compose path, image tag, openclaw.json summary, and the approve command for the next run.
Warning: running openclaw devices approve on the host while the config volume lives only in the container yields a false negative: the command succeeds but the Gateway still does not see it.
The three bullets below are common review bands from community and production; replace them with real monitoring from your hosts and add them to README so new hires do not repeat the same pits.
| # | Pre-launch check | Pass criteria |
|---|---|---|
| 01 | ports bind only 127.0.0.1 | Public direct access to 18789 is closed |
| 02 | env_file matches secret injection paths | Model provider keys are readable in the container |
| 03 | mem_limit and swap policy documented | One stress round with no 137 |
| 04 | healthcheck.start_period ≥ 360s | No restart storm inside the first-run window |
| 05 | openclaw.json has gateway.mode local | Illegal keys removed |
| 06 | Reverse proxy TLS and HSTS policy | No mixed-content warnings in the browser |
| 07 | allowedOrigins covers real visitor origins | Control UI shows no non-loopback errors |
| 08 | Device pairing verified inside the container | devices list empty or all approved |
| 09 | Backup .openclaw path and version | Recoverable from a one-page runbook |
| 10 | Aligned with channel docs | IM or webhook callbacks reachable |
services:
openclaw:
image: ghcr.io/openclaw/openclaw:latest
restart: unless-stopped
mem_limit: 2g
ports:
- "127.0.0.1:18789:18789"
volumes:
- ${HOME}/.openclaw:/home/node/.openclaw
env_file:
- .env
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:18789/health"]
interval: 60s
timeout: 15s
retries: 5
start_period: 360s
Running the Gateway on a laptop long term is often dragged down by sleep, lid close, and flaky uplink; home broadband alone rarely yields an enforceable SLA. By contrast, a cloud Mac Mini billed by period fits hybrid flows that pair OpenClaw on a VPS with macOS or Xcode-side work.
Common mistake: treating 0.0.0.0:18789 as “easier debugging”; on a public VPS that exposes the control plane to scanners.
If you want a stable Docker gateway online while moving heavy compiles or device work into a contract-grade macOS environment, self-owned capex is often weaker on procurement and multi-site sync. For 7×24 nodes with enforceable specs, VpsMesh cloud Mac Mini rental is usually the better fit: draw the split between VPS OpenClaw and dedicated remote Mac in your architecture, then use the pricing page and order page for capacity review instead of relying on verbal promises alone.
Start by checking host dmesg and whether mem_limit triggered OOM, and reserve the first-run WASM window. Review resource baselines together with the OpenClaw v2026.4 install guide.
First list real HTTPS origins under gateway.controlUi.allowedOrigins in openclaw.json and confirm the reverse proxy is not forwarding a wrong Host upstream. For the full install and doctor flow, see the Gateway install troubleshooting checklist.
Use docker compose exec with the same user and volume mounts as the Gateway, then run openclaw devices list; if it still misbehaves, follow the three-part runtime troubleshooting article. For sizing review, open the pricing page and order page; connectivity topics live in the help center.