openclaw fleet
openclaw fleet manages complete OpenClaw instances called cells. Each cell has its own Gateway, state, credentials, channel accounts, container, and loopback-only host port. Use one cell for each tenant trust boundary; do not use one shared Gateway as a hostile multi-tenant boundary.
Fleet is experimental. Command names, flags, output shapes, and the container profile can change between releases without a deprecation window.
Fleet supports Docker and Podman. The default image is ghcr.io/openclaw/openclaw:latest.
Fleet is tested on Linux and macOS hosts. Windows hosts are currently untested.
Quick start
fleet create prints the generated Gateway token once along with the cell URL. Store the token immediately, then configure each tenant’s channel accounts inside that tenant’s cell.
Tenant IDs
Tenant IDs must match:../acme are rejected.
The ID becomes part of the container name: openclaw-cell-<tenant>.
fleet create
Create a cell and start it:
--env:
Create options
Automatic allocation selects the first unused registry port at or above
19100. Fleet rejects duplicate tenant IDs and explicit ports already assigned to another cell.
Image references are passed as one container-runtime argument. Empty references and values beginning with - are rejected so an image cannot be interpreted as a Docker or Podman option.
The selected Docker or Podman endpoint must be local. Fleet rejects remote Docker contexts, DOCKER_HOST endpoints, and remote Podman services before reserving a port or creating local state. Remote cell hosts are not supported.
When Fleet starts a new cell, create waits up to about a minute for its Gateway to answer /healthz. If the cell does not become healthy, Fleet leaves its container and registry row intact for fleet status, fleet logs, or explicit removal. --no-start skips this health gate. The generated Gateway token of an unhealthy new cell is not lost - it remains in the container environment (docker|podman inspect), and because the cell has served no traffic yet, fleet rm --force followed by a fresh create is always a safe alternative.
Pinning by digest
Create and upgrade accept digest-pinned image references such as--image ghcr.io/openclaw/openclaw@sha256:<digest>. Fleet passes the image reference through verbatim to Docker or Podman, which lets an operator keep a cell on immutable image bytes instead of a moving tag.
The create result includes the tenant ID, container name, host port, Gateway token, and local URL. Even in JSON output, treat the result as secret-bearing because it contains the token.
Disk limits
--disk limits only the container writable layer. The bind-mounted per-tenant state and auth directories remain host storage; use host filesystem project quotas when those directories also need a hard limit.
Egress policy
For Docker, keep the bridge mode and enforce outbound policy with host firewall rules such as the
DOCKER-USER chain.
fleet list
List cells in tenant-ID order:
Registry rows remain visible when Docker or Podman is unavailable; only live state becomes
unknown.
fleet status
Inspect one cell:
ok, failed, or skipped. /healthz proves Gateway liveness, not full readiness of every configured channel or plugin. The probe is skipped when there is no usable local endpoint to check.
fleet logs
Stream a cell’s container logs directly to the terminal:
--follow without treating the operator stop as a command failure. Log output is piped through a redaction filter that replaces the cell’s current Gateway token with <redacted> before anything reaches the terminal.
fleet logs has no --json mode because container logs are a raw stdout/stderr stream. For scripts, bound the output with --tail and use ordinary shell redirection or pipelines.
fleet start, fleet stop, and fleet restart
Control an existing cell with its recorded runtime:
fleet upgrade
Re-pull the recorded image and replace the cell container:
--env. Mounted state survives container replacement; image-default environment can change with the target image.
The replacement is committed only after its Gateway answers /healthz on the cell’s loopback port, matching the health contract the official compose file uses. A replacement that exits, crash-loops, or fails to become healthy within about a minute is removed and the previous container is restored, so a broken image does not take down a working cell.
The Gateway token is intentionally not stored in the fleet registry. Before removing the old container, Fleet reads its environment and carries OPENCLAW_GATEWAY_TOKEN into the replacement. Do not manually remove the old container before an upgrade if the token exists nowhere else you control.
fleet backup and fleet restore
Back up one stopped cell:
0600, and must be stored like credentials. Backup refuses a running cell so SQLite state is captured consistently. Restore refuses a running cell unless --force is supplied, replaces only that tenant’s state, rotates the Gateway token, and prints the new token once. Fleet backs up one tenant at a time; all-tenant backup is a separate operator action.
Restore needs an existing stopped container because its inspected runtime profile supplies the replacement limits, user mapping, environment provenance, and image. If the registered container was removed out of band, first run fleet rm <tenant> --force without --purge-data, recreate the cell with the intended image and --no-start, then retry restore. The first removal keeps both tenant data directories intact.
Both commands accept --max-bytes <bytes> to bound archived or extracted file data, and both apply the same fixed one-million budget of archive path segments so metadata-only archive bombs cannot exhaust host inodes and every accepted backup stays restorable. Backup accepts --out <path> and both commands support --json.
Archives contain regular files and directories only. Backup never follows or stores symlinks, hard links, sockets, or device nodes; skipped counts are reported in the result. Restore rejects archives containing any other entry type. Recreatable symlink trees such as workspace node_modules must be reinstalled inside the cell after a restore.
fleet doctor
Audit every cell or one tenant without changing runtime or filesystem state:
fleet rm
Remove a stopped cell from the runtime and registry while keeping tenant data:
--force:
--purge-data requires --force. Before recursive deletion, Fleet resolves both Fleet-owned roots and both per-tenant directories. Each target must be the exact expected tenant leaf, strictly inside its root, and not a symlink. These containment checks prevent a corrupted registry path or cross-tenant symlink from redirecting deletion elsewhere.
Purge is retryable when an exact expected tenant directory is already absent. This lets a later invocation finish cleanup after a partial filesystem failure without relaxing the path checks for directories that still exist.
Storage and container layout
Cell state and auth-profile encryption keys use separate per-tenant host paths under the active OpenClaw state directory:/home/node/.openclaw. The second is mounted at /home/node/.config/openclaw, matching the official Docker setup’s encryption-key mount. The encryption key is therefore not exposed beneath the ordinary state mount or included when only the cell-state directory is backed up or shared. Both directories survive normal removal and upgrade; fleet rm --purge-data --force deletes both after separate containment checks.
Before first start, Fleet initializes the cell config with gateway.mode=local, token auth, the LAN container bind, and Control UI origins for the allocated host port. The token value is not written to that config; it remains in the container environment.
Fleet pins the official image’s container paths with these environment values:
The official image defaults to the non-root
node user with UID 1000. Fleet keeps the private 0700 bind mounts writable without making them world-accessible. Rootful Docker runs the cell with the invoking non-root UID and GID; rootless Docker uses container UID 0, which maps to the invoking unprivileged host user inside the daemon’s user namespace. Podman uses keep-id with the invoking UID and GID. When Fleet itself runs as root against a rootful runtime, it retains the image user and assigns the initial mount files to UID/GID 1000.
On SELinux hosts, Docker and Podman mounts receive a private :Z relabel. If you restore or relocate cell data, keep the bind-mounted paths writable by the effective container user. The profile is rootless-friendly, but Docker or Podman must already be configured for rootless operation on the host; Fleet does not convert a rootful daemon into a rootless one.
Security profile
Fleet applies the following profile to every cell:
Fleet never mounts
/var/run/docker.sock, uses --privileged or host networking, or adds capabilities. The per-cell bridge is a cross-cell separation boundary, not an outbound firewall: cells retain the network egress needed for providers and channels. Front the loopback port with a proxy, SSH tunnel, or tailnet configuration that matches your deployment. http://127.0.0.1:<port> is directly reachable only from the Fleet host.
This profile separates tenant containers, but it does not protect tenants from the Fleet operator, the container runtime administrator, or a compromised host. See Multi-tenant hosting for the complete trust model and stronger isolation options.
Token handling
By default,fleet create generates a cryptographically random 32-character hexadecimal Gateway token and prints it once in the create result. Store it in your approved secret manager and avoid capturing create output in logs.
--gateway-token places a custom token in the local process arguments, which may be retained in shell history or visible in process listings. Prefer the generated token unless an existing secret-management workflow requires a supplied value.
The token and every value passed with --env live in the container environment. Fleet writes them to a short-lived mode-0600 environment file, passes only that file’s path to Docker or Podman, and removes it after the runtime command finishes. Values explicitly typed in openclaw fleet create --gateway-token ... or --env KEY=VALUE can still be visible in the outer openclaw process arguments and shell history.
Container environment values are not hidden from the trusted host operator: Docker or Podman administrators can read them with container inspection. Fleet’s “shown once” note describes normal CLI output, not resistance to a host administrator.