2026 OpenClaw Docker Production Compose · image upgrades · volumes vs systemd baseline

Teams that want container isolation for OpenClaw Gateway—but dread hand-editing paths on upgrade nights usually trade off Compose, named volumes, and image digests. This article lays out a reproducible Docker production path, with a clear split from the on-site Ubuntu 24.04 + systemd + Tunnel bare-metal guide: baselines, docker-compose.yml shape, persistent volumes, image upgrades and health checks, plus a symptom table for common container failures.

01

How this guide splits work with the systemd + Tunnel bare-metal article

The bare-metal path minimizes abstraction: processes on the host, systemd for restarts, tunnel for public ingress. The Docker path swaps the artifact from “directories + unit files” to “images + Compose”: upgrades are mostly pull new digest, recreate containers, and verify mounts still line up. Both can keep Gateway on loopback with TLS at the edge; the fork is whether you accept image supply-chain and volume governance in exchange for reproducibility and finer rollbacks.

If you already standardize registries, scanning, and SBOMs, Compose is often the lowest-friction production shape. A single low-churn VPS may still be cheaper on paper with systemd alone. The six pain points below come from real triage threads—they tell you if you are ready for containers.

  1. 01

    Treating latest as versioning: pin digest or immutable build tags in production; otherwise “it worked last night” maps to no filesystem layer.

  2. 02

    Baking config into images: use env, secrets, or read-only mounts; otherwise every token rotation forces a rebuild and defeats container delivery.

  3. 03

    Anonymous volumes filling disks: unnamed cache volumes from experiments quietly consume /var/lib/docker; prefer named volumes or external object storage.

  4. 04

    Health checks that always return 200: probing only the HTTP port without model/config readiness causes mass 502s after traffic shifts.

  5. 05

    Compose and tunnel configs diverging: ingress still pointing at an old published port makes external symptoms unrelated to container logs.

  6. 06

    Forcing macOS workloads into Linux containers: same as the bare-metal article—Xcode/simulator jobs belong on remote Mac executors, not “magic sandbox” Linux.

If you can answer “where images come from, what volumes store, who owns upgrades,” move on to baselines and templates. If not, write a one-pager in staging before touching production.

When running both paths, share one listen + tunnel topology contract: map containers to 127.0.0.1 on the host loopback and terminate TLS at cloudflared or equivalent. Migrating systemd → Compose then swaps only the process supervisor, not the security boundary.

If you maintain both install scripts and a compose repo, declare a single source of truth—either compose is canonical and CI renders systemd snippets for rare bare-metal cases, or the reverse. Dual-track drift usually shows up as ports, volume names, and env paths changed on one side only.

Containers ease dependency hell but do not remove secret rotation or audit needs. Treating env_file as a safe without permissions and rotation is morally the same as committing tokens—just with a delayed incident date.

02

Decision table: Compose production vs systemd bare metal vs public binding

Review “minutes to install” next to “cost per quarterly upgrade”: containers cost more upfront but repeat better. Replace placeholder names/ports with your real values.

DimensionGateway on public IPsystemd + loopback + Tunnel (bare metal)Docker Compose production
Delivery unitHand-rolled dirs/scriptsUnit + config filesImage digest + compose file
Upgrade pathHigh driftReplace binary/config, roll systemctlPull image, recreate containers, verify volumes
IsolationWeakMedium (users/permissions)Stronger (namespaces/cgroups—not full VMs)
ObservabilityDIYjournald is clearUnify docker logs or sidecar agents
Typical costHighest security riskModerate scriptingImage supply chain + volume ops

Compose declares runtime + persistence + dependency edges; if you refuse to name volumes and tags, you only postpone chaos until the disk alert fires.

03

Prerequisites: Docker Engine, Compose v2, and sizing

On Ubuntu 24.04 LTS or similar, install supported Docker Engine and the Compose v2 plugin (docker compose), avoiding the legacy standalone docker-compose binary that diverges CI from prod. Budget RAM beyond the Gateway for logs, unpack temp, and concurrent connections; watch /var/lib/docker growth separately.

Security matches the bare-metal article: no public listeners for app ports, SSH keys only, default-deny firewall. Containers add namespaces—they do not magically implement TLS or access policy.

  1. 01

    Pin versions: capture docker version and docker compose version in the runbook; align staging/prod minors.

  2. 02

    Log driver + rotation: with default json-file set max-size / max-file so one chatty container cannot fill the disk.

  3. 03

    Dedicated network: declare a custom bridge in compose so only the gateway and tunnel sidecar talk by default.

  4. 04

    Registry discipline: document registry, image name, digest; prod pulls should use the same registry you tested—avoid docker.io in dev and private registry in prod without tracking.

  5. 05

    Volume/bind strategy: read-only config mounts, writable volumes only for runtime/cache, secrets as mode-400 files or Docker secrets.

  6. 06

    Rehearse down/up: in staging run docker compose down then up -d; confirm volumes survive and tunnel upstream still targets the right loopback port.

info

Note: real OpenClaw image names, env keys, and CLI flags follow upstream docs—the YAML below is illustrative; replace placeholders and drop unused keys before prod.

04

Compose skeleton: ports, volumes, health checks, upgrade commands

Publish with 127.0.0.1:host:container so the host loopback feeds the tunnel—same contract as “bind loopback only” in the bare-metal guide. Health checks should prove HTTP readiness; if upstream ships a config validate command, widen start_period so fresh containers are not killed mid-boot.

docker-compose.yml
services:
  openclaw-gateway:
    image: ghcr.io/example/openclaw-gateway@sha256:<DIGEST>
    restart: unless-stopped
    env_file:
      - ./gateway.env
    volumes:
      - openclaw_data:/var/lib/openclaw
      - ./gateway.yaml:/etc/openclaw/gateway.yaml:ro
    ports:
      - "127.0.0.1:8787:8787"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8787/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s

volumes:
  openclaw_data:

Upgrade order:docker compose pull in staging to verify digest; ② back up named volume data or export config; ③ prod docker compose up -d --no-deps --build (drop --build if unused) and watch health; ④ curl from host and through tunnel; ⑤ only then consider docker image prune (dangerous if you still need rollback layers).

Triage stack: container logs for config/permissions; host ss -tlnp for loopback bind; tunnel ingress for the right port. If the internet sees 502 but curl 127.0.0.1 works, suspect routing—not the image first.

For blue/green, briefly run two compose projects (different project names and loopback ports) and shift weights at the tunnel or internal LB; then docker compose -p old_project down or old containers keep volume locks and background work.

If the Gateway must reach a host Unix socket or local model sidecar, scrutinize network_mode: host—it bypasses part of port-map isolation and should be a two-person reviewed change.

warning

Warning: never commit gateway.env with API keys; CI must not print rendered compose env. Wrong volume UID/GID often shows up as flaky health checks rather than instant crashes.

05

Hard numbers and topology notes for the on-call doc

Use these to align with platform on “containers are not free lunch”; replace with your P95 metrics and disk curves.

Alert on restart counts, unhealthy duration, and volume usage—not CPU alone. Gateway-style services often avalanche only after disk or file descriptors exhaust.

  • Layers and disk: frequent pulls of large bases can add tens of GB per month under /var/lib/docker; pair alerts with volume backup policy.
  • Health intervals: too tight amplifies jitter, too loose slows traffic cutover; 30s interval + sane start_period is a common starting point.
  • Rollback window: keep at least one known-good digest and annotate compose with the change ticket—avoid “rollback == latest”.

Linux containers fit OpenClaw gateways, queues, and light orchestration, but Xcode, simulators, and Apple Silicon features hit hard walls—pay forever for compatibility shims or move execution to dedicated Macs. Compared with running gateways on laptops or “temporary” hosts where sleep, updates, and prompts break automation, contracted dedicated remote Macs align long-lived Apple workloads while Linux keeps Docker or systemd for the control plane—failure domains stay clean. For stable agents and build pipelines, NodeMini cloud Mac Mini rental is usually the better answer: keep the gateway in a VPS container and ship heavy work to orderable execution nodes instead of patching the wrong layer.

FAQ

FAQ

Yes—split by environment (staging Compose, prod systemd) or by service. Share the listen+tunnel contract so two gateways never fight for the same loopback port. More OpenClaw posts: category filter.

Check docker logs and whether the health command still matches the new image paths; verify volume permissions and env. For more compute, review rental pricing for remote Mac nodes.

Search the help center for SSH/VNC and networking. For container networking, also check host firewall rules and docker network.