Files
adolf/docs/install/podman.md
alvis bedb527145
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Vendor OpenClaw source as Adolf fork baseline
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
2026-07-05 09:36:54 +00:00

9.2 KiB

summary, read_when, title
summary read_when title
Run OpenClaw in a rootless Podman container
You want a containerized gateway with Podman instead of Docker
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 openclaw CLI is the control plane.
  • Persistent state lives on the host under ~/.openclaw by default.
  • Day-to-day management uses openclaw --container <name> ... instead of sudo -u openclaw, podman exec, or a separate service user.

Prerequisites

  • Podman in rootless mode
  • OpenClaw CLI installed on the host
  • Optional: systemd --user if you want Quadlet-managed auto-start
  • Optional: sudo only if you want loginctl enable-linger "$(whoami)" for boot persistence on a headless host

Quick start

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` | Pre-install plugin dependencies at build time |
| `OPENCLAW_INSTALL_BROWSER` | Pre-install Chromium and Xvfb for browser automation (set to `1`) |

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

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

Or set `OPENCLAW_PODMAN_QUADLET=1`.
```bash ./scripts/run-openclaw-podman.sh launch ```
Starts the container as your current uid/gid with `--userns=keep-id` and bind-mounts your OpenClaw state into the container.
```bash ./scripts/run-openclaw-podman.sh launch setup ```
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.
```bash export OPENCLAW_CONTAINER=openclaw ```
Then normal `openclaw` commands run inside that container automatically:

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

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](#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.

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 serve over openclaw 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.

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:

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

For boot persistence on SSH/headless hosts, enable lingering for your current user:

sudo loginctl enable-linger "$(whoami)"

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, per-agent auth-profiles.json, 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.

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: The container runs with --userns=keep-id and --user <your uid>:<your gid> by default. Ensure the host config/workspace paths are owned by your current user.
  • Gateway start blocked (missing gateway.mode=local): Ensure ~/.openclaw/openclaw.json exists and sets gateway.mode="local". scripts/podman/setup.sh creates this if missing.
  • Container CLI commands hit the wrong target: Use openclaw --container <name> ... explicitly, or export OPENCLAW_CONTAINER=<name> in your shell.
  • openclaw update fails 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, then systemctl --user start openclaw.service. On headless systems you may also need sudo loginctl enable-linger "$(whoami)".
  • SELinux blocks bind mounts: Leave the default mount behavior alone; the launcher auto-adds :Z on Linux when SELinux is enforcing or permissive.