On this page
On this page
Containers
Podman
Run the OpenClaw Gateway in a rootless Podman container, managed by your current non-root user.
The model:
- Podman runs the gateway container.
- Your host
openclawCLI is the control plane. - Persistent state lives on the host under
~/.openclawby default. - Day-to-day management uses
openclaw --container <name> ...instead ofsudo -u openclaw,podman exec, or a separate service user.
Prerequisites
- Podman in rootless mode, with its host init executable (normally
catatonit) available for the launcher's--initflag. On minimal Debian/Ubuntu hosts, install both explicitly:sudo apt-get install podman catatonit. For Podman Machine, the helper must be available inside the machine. - OpenClaw CLI installed on the host
- Optional:
systemd --userif you want Quadlet-managed auto-start - Optional:
sudoonly if you wantloginctl enable-linger "$(whoami)"for boot persistence on a headless host
Quick start
One-time setup
From the repo root, run ./scripts/podman/setup.sh.
This builds openclaw:local in your rootless Podman store (or pulls OPENCLAW_IMAGE / OPENCLAW_PODMAN_IMAGE if set), creates ~/.openclaw/openclaw.json with gateway.mode: "local" if missing, and creates ~/.openclaw/.env with a generated OPENCLAW_GATEWAY_TOKEN if missing.
Optional build-time env vars:
| Var | Effect |
|---|---|
OPENCLAW_IMAGE / OPENCLAW_PODMAN_IMAGE |
Use an existing/pulled image instead of building openclaw:local |
OPENCLAW_IMAGE_APT_PACKAGES |
Install extra apt packages during image build (also accepts legacy OPENCLAW_DOCKER_APT_PACKAGES) |
OPENCLAW_IMAGE_PIP_PACKAGES |
Install extra Python packages during image build; pin versions and use only package indexes you trust |
OPENCLAW_EXTENSIONS |
Compile/package supported selected plugins and install their runtime dependencies |
OPENCLAW_INSTALL_BROWSER |
Pre-install Chromium and Xvfb for browser automation (set to 1) |
For Quadlet-managed setup instead (Linux + systemd user services only):
Or set OPENCLAW_PODMAN_QUADLET=1.
Start the Gateway container
Starts the container as your current uid/gid with --userns=keep-id and bind-mounts your OpenClaw state into the container.
Run onboarding inside the container
Then open http://127.0.0.1:18789/ and use the token from ~/.openclaw/.env.
Model auth: use OpenClaw-managed auth during setup (Anthropic API keys, or OpenAI Codex browser OAuth/device-code auth for Codex-backed OpenAI). The Podman launcher does not mount host CLI credential homes such as ~/.claude or ~/.codex into the setup or gateway container. Existing host CLI logins are same-host convenience paths only -- for container installs, keep provider auth in the mounted ~/.openclaw state that setup manages.
Manage the running container from the host CLI
Then normal openclaw commands run inside that container automatically:
On macOS, Podman machine may make the browser appear non-local to the gateway. If the Control UI reports device-auth errors after launch, use the Tailscale guidance in Podman and Tailscale.
The manual launcher reads only a small allowlist of Podman-related keys from ~/.openclaw/.env and passes explicit runtime env vars to the container; it does not hand the full env file to Podman.
Agent sandbox backend
This page covers running the Gateway itself in a Podman container. Agent sandboxing is separate. Set agents.defaults.sandbox.backend: "podman" to select the native Podman CLI directly. The default "docker" backend remains Docker-only.
Podman reuses the same agents.defaults.sandbox.docker.* container settings as Docker but executes them through the native podman CLI. Browser sandboxes remain Docker-only for now.
See Sandboxing for the config example and image-build command.
Podman and Tailscale
For HTTPS or remote browser access, follow the main Tailscale docs.
Podman-specific notes:
- Keep the Podman publish host at
127.0.0.1. - Prefer host-managed
tailscale serveoveropenclaw gateway --tailscale serve. - On macOS, if local browser device-auth context is unreliable, use Tailscale access instead of ad hoc local tunnel workarounds.
See Tailscale and Control UI.
Reaching model servers on the host
Inside the container, 127.0.0.1 is the container itself. To use a model server running on the host (llama.cpp, LM Studio, Ollama, vLLM, or any OpenAI-compatible endpoint), map the host-gateway alias:
Recent Podman versions also provide host.containers.internal automatically. Reference the host server as http://host.docker.internal:<port>/v1 (or http://host.containers.internal:<port>/v1) in the provider baseUrl.
See Local models for provider configuration and Docker networking, providers, and storage for the Docker equivalent.
Systemd (Quadlet, optional)
If you ran ./scripts/podman/setup.sh --quadlet, setup installs a Quadlet file at ~/.config/containers/systemd/openclaw.container.
| Action | Command |
|---|---|
| Start | systemctl --user start openclaw.service |
| Stop | systemctl --user stop openclaw.service |
| Status | systemctl --user status openclaw.service |
| Logs | journalctl --user -u openclaw.service -f |
After editing the Quadlet file:
For boot persistence on SSH/headless hosts, enable lingering for your current user:
The generated Quadlet service keeps a fixed, hardened default shape: 127.0.0.1 published ports (18789 gateway, 18790 bridge), --bind lan inside the container, keep-id user namespace, OPENCLAW_NO_RESPAWN=1, Restart=on-failure, and TimeoutStartSec=300. It reads ~/.openclaw/.env as a runtime EnvironmentFile for values such as OPENCLAW_GATEWAY_TOKEN, but does not consume the manual launcher's Podman-specific override allowlist. For custom publish ports, publish host, or other container-run flags, use the manual launcher instead, or edit ~/.config/containers/systemd/openclaw.container directly and then reload and restart the service.
Config, env, and storage
- Config dir:
~/.openclaw - Workspace dir:
~/.openclaw/workspace - Token file:
~/.openclaw/.env - Launch helper:
./scripts/run-openclaw-podman.sh
The launch script and Quadlet bind-mount host state into the container: OPENCLAW_CONFIG_DIR -> /home/node/.openclaw, OPENCLAW_WORKSPACE_DIR -> /home/node/.openclaw/workspace. By default those are host directories, not anonymous container state, so openclaw.json, shared and per-agent SQLite auth stores, channel/provider state, sessions, and workspace survive container replacement. Setup also seeds gateway.controlUi.allowedOrigins for 127.0.0.1 and localhost on the published gateway port so the local dashboard works with the container's non-loopback bind.
Useful env vars for the manual launcher (persist these in ~/.openclaw/.env; the launcher reads that file before finalizing container/image defaults):
| Var | Default | Effect |
|---|---|---|
OPENCLAW_PODMAN_CONTAINER |
openclaw |
Container name |
OPENCLAW_PODMAN_IMAGE / OPENCLAW_IMAGE |
openclaw:local |
Image to run |
OPENCLAW_PODMAN_GATEWAY_HOST_PORT |
18789 |
Host port mapped to container 18789 |
OPENCLAW_PODMAN_BRIDGE_HOST_PORT |
18790 |
Host port mapped to container 18790 |
OPENCLAW_PODMAN_PUBLISH_HOST |
127.0.0.1 |
Host interface for published ports |
OPENCLAW_GATEWAY_BIND |
lan |
Gateway bind mode inside the container |
OPENCLAW_PODMAN_USERNS |
keep-id |
keep-id, auto, or host |
If you use a non-default OPENCLAW_CONFIG_DIR or OPENCLAW_WORKSPACE_DIR, set the same variables for both ./scripts/podman/setup.sh and later ./scripts/run-openclaw-podman.sh launch commands -- the repo-local launcher does not persist custom path overrides across shells.
Upgrading images
After you rebuild or pull a new image, restart the container or Quadlet service. On first startup for a new OpenClaw version, the gateway runs safe state and plugin repairs before reporting ready.
If the gateway exits instead of becoming ready, run the same image once with
openclaw doctor --fix against the same mounted state/config, then restart the
gateway normally:
On SELinux hosts, add ,Z to both bind mounts if Podman blocks access to the
mounted state.
After restarting the Gateway with the updated image, run the read-only deployment preflight through the container-aware host CLI:
Useful commands
- Container logs:
podman logs -f openclaw - Stop container:
podman stop openclaw - Remove container:
podman rm -f openclaw - Open dashboard URL from host CLI:
openclaw dashboard --no-open - Health/status via host CLI:
openclaw gateway status --deep(RPC check + extra service scan)
Troubleshooting
- Token generation fails: Setup and launch stop before saving a generated token or starting the container when the selected random source (
openssl, Python, orod) fails. Repair that command and retry. - Init executable missing (
lookup init binary/container-init binary not found on the host): Installcatatoniton the Podman engine host or repair its configuredinit_path/helper_binaries_dirincontainers.conf, then retry. Installing the helper inside the Gateway or sandbox image does not repair the engine host. Keep--initenabled; see Host init prerequisite. - Permission denied (EACCES) on config or workspace: The container runs with
--userns=keep-idand--user <your uid>:<your gid>by default. Check that the host config/workspace paths are owned by your current user. One-offpodman runcommands that touch the same state need the same mapping (--userns=keep-id) — without it the container uid doesn't match files the gateway created, even when the host paths are owned by you. - Gateway start blocked (missing
gateway.mode=local): Check that~/.openclaw/openclaw.jsonexists and setsgateway.mode="local".scripts/podman/setup.shcreates this if missing. - Published port unreachable but startup logs report
ready: The gateway bound to loopback only.gateway.bind: "tailnet"and"loopback"are host-centric (see the Quadlet note above); inside a container there is no Tailscale interface, andtailnetfalls back to loopback-only. Use the Quadlet default (--bind lan) orgateway.bind: "custom"+gateway.customBindHost: "0.0.0.0", and control exposure with published ports. - Config changes rejected with
cannot verify Gateway ownership from this process: The running gateway locksopenclaw.jsonand only admits writes from its own container/PID namespace. Run config-mutating commands through the container-aware host CLI (openclaw --container openclaw config ...) orpodman exec openclaw ...; the same command from a fresh one-off container is rejected. On versions withgateway recover-lock, stop the gateway, then runopenclaw gateway recover-lockto inspect and clear the stale ownership record before mutating config from outside the container. On those versions, heartbeat expiry alone does not prove the previous holder has stopped. - SELinux relabel fails on a bind mount (
failed to change selinux label: insufficient permissions): Rootless Podman applies:Z/:zrelabels as your user and cannot relabel host directories owned by root (for example a read-only/var/log/journalmount). Use a plainromount without a relabel suffix for system directories you don't own; reserve:Zfor state directories your user owns. Note that dropping the relabel only avoids the relabel operation — it doesn't grant access by itself. The files keep their original SELinux labels, and reading them still fails if the container domain isn't permitted to that label. In that case give the container an appropriate policy or remove the mount entirely. - Container restarts after an image update: Run the one-off
openclaw doctor --fixcommand in Upgrading images, then start the gateway again. - Container CLI commands hit the wrong target: Use
openclaw --container <name> ...explicitly, or exportOPENCLAW_CONTAINER=<name>in your shell. openclaw updatefails with--container: Expected. Rebuild/pull the image, then restart the container or the Quadlet service.- Quadlet service does not start: Run
systemctl --user daemon-reload, thensystemctl --user start openclaw.service. On headless systems you may also needsudo loginctl enable-linger "$(whoami)". - SELinux blocks bind mounts: Leave the default mount behavior alone; the launcher auto-adds
:Zon Linux when SELinux is enforcing or permissive.