Run OpenClaw in a Rootless Podman Container

This page explains how to run the OpenClaw Gateway inside a rootless Podman container using your existing user. It covers prerequisites, setup, and management commands for developers and system administrators.

Read this when

  • You want a containerized gateway with Podman instead of Docker

Run the OpenClaw Gateway inside a rootless Podman container, with your existing non-root user handling management.

How the pieces fit together:

  • Podman is responsible for running the gateway container.
  • Your host-side openclaw CLI acts as the control plane.
  • Persistent data is stored on the host, defaulting to ~/.openclaw.
  • Routine administration relies on openclaw --container <name> ... rather than sudo -u openclaw, podman exec, or a dedicated service account.

Prerequisites

  • Podman operating in rootless mode
  • OpenClaw CLI present on the host
  • Optional: systemd --user when you want Quadlet-driven auto-start
  • Optional: sudo solely if you need loginctl enable-linger "$(whoami)" for boot-time persistence on a headless machine

Quick start

One-time setup

Execute ./scripts/podman/setup.sh from the repository root.

That command compiles openclaw:local into your rootless Podman storage (or fetches OPENCLAW_IMAGE / OPENCLAW_PODMAN_IMAGE when those are configured), establishes ~/.openclaw/openclaw.json with gateway.mode: "local" if it does not exist, and sets up ~/.openclaw/.env with a freshly generated OPENCLAW_GATEWAY_TOKEN when needed.

Environment variables you can set at build time:

VarEffect
OPENCLAW_IMAGE / OPENCLAW_PODMAN_IMAGELeverage an existing or pulled image instead of compiling openclaw:local
OPENCLAW_IMAGE_APT_PACKAGESAdd extra apt packages during the image build (the legacy OPENCLAW_DOCKER_APT_PACKAGES form is also accepted)
OPENCLAW_IMAGE_PIP_PACKAGESAdd extra Python packages during the image build; pin versions and rely only on package indexes you trust
OPENCLAW_EXTENSIONSCompile and package selected supported plugins, then install their runtime dependencies
OPENCLAW_INSTALL_BROWSERPre-load Chromium and Xvfb for browser automation (set to 1)

For a Quadlet-managed setup instead (Linux plus systemd user services only):

./scripts/podman/setup.sh --quadlet

Alternatively, set OPENCLAW_PODMAN_QUADLET=1.

Start the Gateway container

./scripts/run-openclaw-podman.sh launch

The container launches with your current uid/gid via --userns=keep-id, and your OpenClaw state is bind-mounted into it.

Run onboarding inside the container

./scripts/run-openclaw-podman.sh launch setup

After that, navigate to http://127.0.0.1:18789/ and grab the token found in ~/.openclaw/.env.

Model auth: during setup, rely on OpenClaw-managed authentication (Anthropic API keys, or OpenAI Codex browser OAuth/device-code auth for Codex-backed OpenAI). The Podman launcher does not expose host CLI credential homes like ~/.claude or ~/.codex to either the setup or gateway container. Host CLI logins that already exist are just same-host conveniences; for container installs, keep provider auth inside the mounted ~/.openclaw state that setup manages.

Manage the running container from the host CLI

export OPENCLAW_CONTAINER=openclaw

From there, standard openclaw commands execute inside that container without extra steps:

openclaw dashboard --no-open
openclaw gateway status --deep   # includes extra service scan
openclaw doctor
openclaw channels login

On macOS, the Podman machine can make the browser look non-local to the gateway. If the Control UI shows device-auth errors after startup, follow the Tailscale guidance in Podman and Tailscale.

The manual launcher reads only a narrow allowlist of Podman-related keys from ~/.openclaw/.env and passes explicit runtime env vars to the container; the full env file is never handed to Podman.

Agent sandbox backend

This page is about running the Gateway itself in a Podman container. Agent sandboxing is a separate concern. Pick agents.defaults.sandbox.backend: "podman" to use the native Podman CLI directly. The default "docker" backend still supports Docker only.

Podman applies the same agents.defaults.sandbox.docker.* container settings as Docker but runs them through the native podman CLI. Browser sandboxes remain Docker-only for now.

Check Sandboxing for the config example and the image-build command.

Podman and Tailscale

For HTTPS or remote browser access, refer to the main Tailscale documentation.

Notes specific to Podman:

  • Keep the Podman publish host at 127.0.0.1.
  • Favor host-managed tailscale serve over openclaw gateway --tailscale serve.
  • On macOS, if local browser device-auth context is unreliable, go with Tailscale access rather than ad hoc local tunnel workarounds.

See Tailscale and Control UI.

Systemd (Quadlet, optional)

Running ./scripts/podman/setup.sh --quadlet causes setup to place a Quadlet file at ~/.config/containers/systemd/openclaw.container.

ActionCommand
Startsystemctl --user start openclaw.service
Stopsystemctl --user stop openclaw.service
Statussystemctl --user status openclaw.service
Logsjournalctl --user -u openclaw.service -f

Once the Quadlet file has been modified:

systemctl --user daemon-reload
systemctl --user restart openclaw.service

To make the service persist across reboots on SSH/headless hosts, turn on lingering for the current user:

sudo loginctl enable-linger "$(whoami)"

The Quadlet service that gets generated ships with a locked-down, predictable configuration: 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 pulls ~/.openclaw/.env as a runtime EnvironmentFile for settings such as OPENCLAW_GATEWAY_TOKEN, yet it ignores the manual launcher's Podman-specific override allowlist. When you need custom publish ports, a different publish host, or other container-run options, go with the manual launcher instead, or change ~/.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

Host state is bind-mounted into the container by the launch script and Quadlet: OPENCLAW_CONFIG_DIR -> /home/node/.openclaw, OPENCLAW_WORKSPACE_DIR -> /home/node/.openclaw/workspace. By default these point to host directories rather than anonymous container state, which means openclaw.json, per-agent auth-profiles.json, channel/provider state, sessions, and workspace all survive container replacement. Setup also populates gateway.controlUi.allowedOrigins for 127.0.0.1 and localhost on the published gateway port, ensuring the local dashboard works with the container's non-loopback bind.

Environment variables that are handy for the manual launcher (save these in ~/.openclaw/.env; the launcher reads that file before it finalizes container/image defaults):

VarDefaultEffect
OPENCLAW_PODMAN_CONTAINERopenclawContainer name
OPENCLAW_PODMAN_IMAGE / OPENCLAW_IMAGEopenclaw:localImage to run
OPENCLAW_PODMAN_GATEWAY_HOST_PORT18789Host port mapped to container 18789
OPENCLAW_PODMAN_BRIDGE_HOST_PORT18790Host port mapped to container 18790
OPENCLAW_PODMAN_PUBLISH_HOST127.0.0.1Host interface for published ports
OPENCLAW_GATEWAY_BINDlanGateway bind mode inside the container
OPENCLAW_PODMAN_USERNSkeep-idkeep-id, auto, or host

When OPENCLAW_CONFIG_DIR or OPENCLAW_WORKSPACE_DIR deviates from the default, apply the same variables to both ./scripts/podman/setup.sh and subsequent ./scripts/run-openclaw-podman.sh launch commands, since the repo-local launcher does not carry custom path overrides across shells.

Upgrading images

Once a fresh image has been pulled or rebuilt, bring the container, or the Quadlet service, back up. When a new OpenClaw version boots for the first time, the gateway performs safe state and plugin repairs before it signals readiness.

Should the gateway terminate rather than reach a ready state, execute that same image once with openclaw doctor --fix while pointing at the identical mounted state and config, and then relaunch the gateway normally:

OPENCLAW_CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}"
OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$OPENCLAW_CONFIG_DIR/workspace}"
OPENCLAW_PODMAN_IMAGE="${OPENCLAW_PODMAN_IMAGE:-${OPENCLAW_IMAGE:-openclaw:local}}"

podman run --rm -it \
  --userns=keep-id \
  --user "$(id -u):$(id -g)" \
  -e HOME=/home/node \
  -e NPM_CONFIG_CACHE=/home/node/.openclaw/.npm \
  -v "$OPENCLAW_CONFIG_DIR:/home/node/.openclaw:rw" \
  -v "$OPENCLAW_WORKSPACE_DIR:/home/node/.openclaw/workspace:rw" \
  "$OPENCLAW_PODMAN_IMAGE" \
  openclaw doctor --fix

On hosts where SELinux is active, append ,Z to both bind mounts if Podman denies access to the mounted state.

Once the Gateway has been restarted with the updated image, invoke the read-only deployment preflight through the container-aware host CLI:

export OPENCLAW_CONTAINER=openclaw
openclaw doctor --json

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 probe + extra service scan)

Troubleshooting

  • Permission denied (EACCES) on config or workspace: By default, the container operates with --userns=keep-id and --user <your uid>:<your gid>. Verify that your current user owns the host config and workspace paths.
  • Gateway start blocked (missing gateway.mode=local): Confirm that ~/.openclaw/openclaw.json is present and defines gateway.mode="local". If it is absent, scripts/podman/setup.sh will generate it.
  • Container restarts after an image update: Execute the one-off openclaw doctor --fix command from Upgrading images, and then relaunch the gateway.
  • Container CLI commands hit the wrong target: Pass openclaw --container <name> ... explicitly, or set OPENCLAW_CONTAINER=<name> in your shell environment.
  • openclaw update fails with --container: This is normal. Rebuild or pull the image, then restart the container or the Quadlet service.
  • Quadlet service does not start: Run systemctl --user daemon-reload followed by systemctl --user start openclaw.service. On systems without a display, sudo loginctl enable-linger "$(whoami)" might also be required.
  • SELinux blocks bind mounts: Keep the default mount behavior unchanged; the launcher automatically adds :Z on Linux when SELinux is enforcing or permissive.
1,451 words · updated Aug 14, 2026