Installing OpenClaw with Docker: What Actually Persists
Before anything else, OpenClaw’s own documentation opens its Docker page with a sentence most guides skip:
“Docker is optional. Use it for an isolated, throwaway gateway environment or a host without local installs. If you already develop on your own machine, use the normal install flow instead.”
That’s worth taking seriously. Containerising buys you a restart policy, a clean upgrade path and resource isolation. It costs you a layer of indirection over an agent that already has a lot of moving parts. If you’re running this on your laptop, the plain install is the better tool.
If you’re running it on a server, read on — but not for the reason you’ve probably been told.
The persistence warning you’ll read is wrong
The common advice runs: published compose files use anonymous volumes, so one careless docker compose down -v destroys your config, therefore switch to named volumes.
Check what OpenClaw’s compose file actually does:
“Docker Compose bind-mounts
OPENCLAW_CONFIG_DIRto/home/node/.openclaw,OPENCLAW_WORKSPACE_DIRto/home/node/.openclaw/workspace, andOPENCLAW_AUTH_PROFILE_SECRET_DIRto/home/node/.config/openclaw, so those paths survive container replacement.”
Those are bind mounts to host directories, and docker compose down -v does not remove bind mounts. The -v flag removes named and anonymous volumes; a directory on your host filesystem isn’t either. So the specific disaster the warning describes doesn’t apply to the shipped configuration — and swapping to named volumes would actually make you more exposed to down -v, not less.
The docs also note a sensible fallback: when the variables are unset, compose falls back under ${HOME}, or /tmp if HOME is missing, so docker compose up never produces an empty-source volume spec on a bare environment.
What genuinely bites instead is a different thing entirely. There are two separate state locations, and people back up one:
-
OPENCLAW_CONFIG_DIRholdsopenclaw.json,agents/<agentId>/agent/auth-profiles.jsonfor stored provider auth, and.envfor runtime secrets likeOPENCLAW_GATEWAY_TOKEN. -
OPENCLAW_AUTH_PROFILE_SECRET_DIRholds the local encryption key for OAuth-backed auth profile tokens.
OpenClaw’s guidance is explicit: keep that secret directory with your Docker host state, but separate from OPENCLAW_CONFIG_DIR. Copy the config directory to a new host without it and your stored provider auth is encrypted material you can no longer decrypt. That’s the restore that fails at the worst moment, and it’s the one worth testing.
Before you build
Two prerequisites from the docs that catch small servers:
- Docker Engine plus Docker Compose v2
-
At least 2GB of RAM to build the image. The docs are specific:
pnpm install“may be OOM-killed on 1 GB hosts with exit 137.”
That exit code is the kernel killing the build for memory — the same silent failure mode that catches people at runtime. On a 1GB box the build dies with no useful error. Either size up or skip the build entirely and pull a pre-built image, which is the better answer anyway.
Getting the image
You don’t have to build it. Pre-built images are published to the GitHub Container Registry first, which the docs describe as the primary registry “for release automation, pinned deployments, and provenance checks,” with a Docker Hub mirror published by the same release:
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
./scripts/docker/setup.sh
Building locally produces openclaw:local instead. Prefer GHCR if you care about provenance, and pin an exact version rather than latest for anything you’d be annoyed to have change under you.
Health checks: use the right endpoint
This is the part worth getting right, because the obvious choice is the wrong one.
The image ships a built-in HEALTHCHECK that pings /healthz, and repeated failures mark the container unhealthy so an orchestrator can restart it. There are three probes, and they answer different questions:
| Endpoint | What it means |
|---|---|
/healthz |
Liveness — the process is alive |
/startupz |
Startup and traffic admission |
/readyz |
Deep, channel-aware readiness |
curl -fsS http://127.0.0.1:18789/healthz
curl -fsS http://127.0.0.1:18789/startupz
curl -fsS http://127.0.0.1:18789/readyz
Use /startupz for an orchestrator readiness probe, and the docs give the reason plainly: it means “a failed channel account does not remove the otherwise healthy Gateway and Control UI from service.” Point your readiness probe at /readyz instead and a single broken WhatsApp link will take your entire dashboard out of rotation — the agent stops being reachable because one of its channels is unhappy.
/readyz is the right probe for monitoring, where you do want to know that a channel is down. It’s the wrong one for deciding whether to serve traffic.
For a deeper snapshot, authenticated:
docker compose exec openclaw-gateway sh -lc 'node dist/index.js gateway health --token "$OPENCLAW_GATEWAY_TOKEN"'
Binding: don’t use an IP address
A small trap with a confusing failure. OpenClaw’s gateway.bind takes mode values, not host aliases:
-
lan— host browser and host CLI reach the published port. This is whatsetup.shdefaults to. -
loopback— only processes inside the container network namespace reach it directly -
custom,tailnet,auto
Writing 0.0.0.0 or 127.0.0.1 there is not the same thing as the mode you meant. Set the mode, and control public exposure with Docker’s port publishing and your firewall.
Upgrading without losing state
The good news first: replace the image, keep the mounted state, and the new gateway runs startup-safe upgrade migrations and plugin convergence before it reports ready. Routine upgrades shouldn’t need a manual repair pass.
docker compose pull
docker compose up -d
If a migration can’t complete safely, the gateway exits rather than reporting healthy. That’s the right design — it fails closed instead of serving from half-migrated state — but with a restart policy in place it looks like a crash loop, so know what you’re seeing. The recovery is to run the same image once as a one-off doctor against the same mounts:
docker run --rm -v <openclaw-state>:/home/node/.openclaw <image> openclaw doctor --fix
Then restart the gateway normally, and run the read-only preflight:
docker compose run --rm openclaw-cli doctor --json
Prove your persistence works before you need it. The test takes a minute:
docker compose down # note: no -v needed, and don't add it casually
docker compose up -d
If it comes back still linked to your channels and still holding your config, persistence is real. If it asks you to scan a QR code again, it isn’t — and you’ve learned that on a Tuesday rather than during an incident.
On a public server
Two things specific to running this on a VPS.
The firewall does not work the way you think. OpenClaw’s docs point at the DOCKER-USER chain for exactly this reason: Docker publishes ports past ufw, because container traffic is routed before the chains ufw uses. A ufw deny rule on a Docker-published port reads correctly and does nothing. Filter via DOCKER-USER, publish to 127.0.0.1 explicitly, or use your provider’s network firewall, which sits outside the host entirely. Then verify from outside rather than assuming.
Watch the disk. The docs name the growth hotspots: media/, per-agent SQLite databases, legacy session JSONL transcripts, the shared SQLite state database, plugin package roots, and rolling logs under /tmp/openclaw/. An agent that has been running for months on a 40GB disk will find them.
Where to run it
OpenClaw’s own installation docs include a Hetzner Docker VPS page, which tells you something about where this pattern is commonly deployed — Hetzner is the cheapest per gigabyte when its CX tier is in stock — worth checking before you commit, because that tier was showing as unavailable when we last looked, and the CPX line you’d fall back to is several times dearer. A plain Docker host is exactly its strength. Size 4GB for a text-only agent and 8GB if browser automation is anywhere in your plans.
If you’d rather not administer the box at all, Hostinger ships a one-click OpenClaw deployment where the Docker layer is managed for you — we cover the two products and what each leaves you responsible for separately, because the distinction matters more than the price.
For the manual, non-Docker path with hardening done first, see self-hosting OpenClaw on a VPS; for sizing, best VPS for OpenClaw.
How we checked this
Every claim about persistence, health endpoints, bind modes, upgrade behaviour, image registries, build memory requirements and disk hotspots is from OpenClaw’s own Docker installation documentation, read in August 2026. The docker compose down -v behaviour — that it removes named and anonymous volumes but not bind mounts to host paths — is standard Docker semantics, and it’s why the widely-repeated warning about this compose file doesn’t hold. The ufw interaction is quoted from Docker’s own documentation elsewhere in our coverage.
What we did not do: we didn’t run this deployment end to end for this article, so treat the sequences as a careful reading of the documentation rather than a lab report — with one exception worth repeating, which is that the teardown test above is the one thing you should actually run yourself. It takes a minute and it’s the only way to know your state survives, on your host, with your paths.
We also haven’t audited what the pre-built images contain; the docs reference image contents and security scanning, and pinning a version is the sensible default for anything you depend on.
The two hosting links above are affiliate links. Nothing in the install, persistence or upgrade sections requires spending anything.
FAQ
Does docker compose down -v delete my OpenClaw config?
Not with the shipped compose file. It bind-mounts config, workspace and auth-secret directories to host paths, and -v removes named and anonymous volumes rather than bind mounts. Still avoid the habit — and test a plain down then up -d to confirm your setup persists.
Where does OpenClaw store its Docker state?
OPENCLAW_CONFIG_DIR holds openclaw.json, stored provider auth profiles and the .env with your gateway token. OPENCLAW_AUTH_PROFILE_SECRET_DIR separately holds the encryption key for OAuth token material — back up both, and keep them separate as the docs advise.
Which health endpoint should my orchestrator use?
/startupz. It admits traffic without treating a failed channel account as a dead gateway, so one broken messaging link doesn’t pull your Control UI out of service. Use /readyz for monitoring, where you do want channel failures surfaced.
How do I upgrade the OpenClaw container?
Pull the new image and recreate while keeping the mounted state — the gateway runs its own startup migrations before reporting ready. If it exits instead of becoming healthy, run the same image once with openclaw doctor --fix against the same mounts, then start it normally.
Why did my image build fail with exit 137?
Memory. The docs require at least 2GB to build, and note pnpm install may be OOM-killed on 1GB hosts with exactly that exit code. Pull a pre-built image from GHCR instead of building on a small server.
Should I use Docker for OpenClaw at all?
OpenClaw says Docker is optional and recommends the normal install if you’re on your own machine. Containerise for a server deployment, an isolated throwaway environment, or a host where you’d rather not install Node — not by default.
Is latest a safe tag to run?
For a hobby instance, fine. For anything you depend on, pin an exact version — GHCR is the primary registry for pinned deployments and provenance, with Docker Hub as a mirror of the same release.