Files
openclaw/docs/install/podman.md
Sally O'Malley df5b9ef0c6 update podman setup and docs (#55388)
* update podman setup and docs

Signed-off-by: sallyom <somalley@redhat.com>

* podman: persist runtime env defaults

Co-authored-by: albertxos <kickban3000@gmail.com>
Signed-off-by: sallyom <somalley@redhat.com>

* podman: harden env and path handling, other setup updates

Signed-off-by: sallyom <somalley@redhat.com>

* podman: allow symlinked home path components

Signed-off-by: sallyom <somalley@redhat.com>

* update podman docs

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: albertxos <kickban3000@gmail.com>
2026-03-27 11:47:35 -04:00

10 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

Podman

Run the OpenClaw Gateway in a rootless Podman container, managed by your current non-root user.

The intended model is:

  • 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`. Start the container with `./scripts/run-openclaw-podman.sh launch`. Run `./scripts/run-openclaw-podman.sh launch setup`, then open `http://127.0.0.1:18789/`. Set `OPENCLAW_CONTAINER=openclaw`, then use normal `openclaw` commands from the host.

Setup details:

  • ./scripts/podman/setup.sh builds openclaw:local in your rootless Podman store by default, or uses OPENCLAW_IMAGE / OPENCLAW_PODMAN_IMAGE if you set one.
  • It creates ~/.openclaw/openclaw.json with gateway.mode: "local" if missing.
  • It creates ~/.openclaw/.env with OPENCLAW_GATEWAY_TOKEN if missing.
  • For manual launches, the helper 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.

Quadlet-managed setup:

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

Quadlet is a Linux-only option because it depends on systemd user services.

You can also set OPENCLAW_PODMAN_QUADLET=1.

Optional build/setup env vars:

  • OPENCLAW_IMAGE or OPENCLAW_PODMAN_IMAGE -- use an existing/pulled image instead of building openclaw:local
  • OPENCLAW_DOCKER_APT_PACKAGES -- install extra apt packages during image build
  • OPENCLAW_EXTENSIONS -- pre-install extension dependencies at build time

Container start:

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

The script starts the container as your current uid/gid with --userns=keep-id and bind-mounts your OpenClaw state into the container.

Onboarding:

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

Then open http://127.0.0.1:18789/ and use the token from ~/.openclaw/.env.

Host CLI default:

export OPENCLAW_CONTAINER=openclaw

Then commands such as these will run inside that container automatically:

openclaw dashboard --no-open
openclaw gateway status --deep
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, prefer the SSH tunnel flow in macOS Podman SSH tunnel. For remote HTTPS access, use the Tailscale guidance in Podman + Tailscale.

macOS Podman SSH tunnel

On macOS, Podman machine can make the browser appear non-local to the gateway even when the published port is only on 127.0.0.1.

For local browser access, use an SSH tunnel into the Podman VM and open the tunneled localhost port instead.

Recommended local tunnel port:

  • 28889 on the Mac host
  • forwarded to 127.0.0.1:18789 inside the Podman VM

Start the tunnel in a separate terminal:

ssh -N \
  -i ~/.local/share/containers/podman/machine/machine \
  -p <podman-vm-ssh-port> \
  -L 28889:127.0.0.1:18789 \
  core@127.0.0.1

In that command, <podman-vm-ssh-port> is the Podman VM's SSH port on the Mac host. Check your current value with:

podman system connection list

Allow the tunneled browser origin once. This is required the first time you use the tunnel because the launcher can auto-seed the Podman-published port, but it cannot infer your chosen browser tunnel port:

OPENCLAW_CONTAINER=openclaw openclaw config set gateway.controlUi.allowedOrigins \
  '["http://127.0.0.1:18789","http://localhost:18789","http://127.0.0.1:28889","http://localhost:28889"]' \
  --strict-json
podman restart openclaw

That is a one-time step for the default 28889 tunnel.

Then open:

http://127.0.0.1:28889/

Notes:

  • 18789 is usually already occupied on the Mac host by the Podman-published gateway port, so the tunnel uses 28889 as the local browser port.
  • If the UI asks for pairing approval, prefer explicit container-targeted or explicit-URL commands so the host CLI does not fall back to local pairing files:
openclaw --container openclaw devices list
openclaw --container openclaw devices approve --latest
  • Equivalent explicit-URL form:
openclaw devices list \
  --url ws://127.0.0.1:28889 \
  --token "$(sed -n 's/^OPENCLAW_GATEWAY_TOKEN=//p' ~/.openclaw/.env | head -n1)"

Podman + Tailscale

For HTTPS or remote browser access, follow the main Tailscale docs.

Podman-specific note:

  • Keep the Podman publish host at 127.0.0.1.
  • Prefer host-managed tailscale serve over openclaw gateway --tailscale serve.
  • For local macOS browser access without HTTPS, prefer the SSH tunnel section above.

See:

Systemd (Quadlet, optional)

If you ran ./scripts/podman/setup.sh --quadlet, setup installs a Quadlet file at:

~/.config/containers/systemd/openclaw.container

Useful commands:

  • 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)"

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 config and workspace survive container replacement. The Podman 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:

  • OPENCLAW_PODMAN_CONTAINER -- container name (openclaw by default)
  • OPENCLAW_PODMAN_IMAGE / OPENCLAW_IMAGE -- image to run
  • OPENCLAW_PODMAN_GATEWAY_HOST_PORT -- host port mapped to container 18789
  • OPENCLAW_PODMAN_BRIDGE_HOST_PORT -- host port mapped to container 18790
  • OPENCLAW_PODMAN_PUBLISH_HOST -- host interface for published ports; default is 127.0.0.1
  • OPENCLAW_GATEWAY_BIND -- gateway bind mode inside the container; default is lan
  • OPENCLAW_PODMAN_USERNS -- keep-id (default), auto, or host

The manual launcher reads ~/.openclaw/.env before finalizing container/image defaults, so you can persist these there.

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.

Quadlet note:

  • The generated Quadlet service intentionally keeps a fixed, hardened default shape: 127.0.0.1 published ports, --bind lan inside the container, and keep-id user namespace.
  • It still reads ~/.openclaw/.env for gateway runtime env such as OPENCLAW_GATEWAY_TOKEN, but it does not consume the manual launcher's Podman-specific override allowlist.
  • If you need custom publish ports, publish host, or other container-run flags, use the manual launcher or edit ~/.config/containers/systemd/openclaw.container directly, then reload and restart the service.

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

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.