This guide walks through setting up OpenClaw in a rootless Podman container on Arch Linux — no root, no Docker daemon, no unnecessary risk.

What is OpenClaw?

OpenClaw is an open-source personal AI assistant that you run on your own hardware. It connects to the messaging platforms you already use — Signal, Telegram, Discord, Slack, WhatsApp, iMessage — and gives you a single always-on assistant that can read files, run commands, browse the web, manage your calendar, and basically act as a second brain. Think of it as self-hosted ChatGPT, but with actual access to your stuff.

It supports multiple AI providers (Anthropic, OpenAI, local models via Ollama) and most people run it directly on their machine — Mac Minis, laptops, Raspberry Pis — installed as a user-level service. That works great for most setups.

But here’s the thing: if it’s running on bare metal as your user, the agent has access to everything on your computer. Your SSH keys, your credentials, your home directory, your browsing history — all of it. And remember, every command the agent runs, every file it reads, that context is being sent to whichever AI provider is powering it — Anthropic, OpenAI, or whoever else. That might not be something you want to share. A container gives you control over exactly what the agent can see and touch.

A rootless Podman container keeps the agent isolated without the complexity of setting up and maintaining a full VM. It’s not quite as strong a boundary as a VM — containers share the host kernel — but it’s far less to manage. The agent runs as a non-root node user inside a container that itself has no root privileges on the host. If something goes wrong — a bad prompt, unintended code execution, whatever — it’s stuck in an unprivileged container, and even a container escape lands as an unprivileged user. Your host and the rest of your network stay untouched.

The principle is simple: give it what it needs, wall off everything else.

The Ephemeral Box

Every time you rebuild the image, you get a fresh container. New OS, new packages, clean slate. Nothing persists inside the container itself — no leftover state, no “it worked a minute ago.” If something breaks, you rebuild and restart — no reinstalling your OS, no trying to fix a broken box. Just a clean slate in under a minute.

But OpenClaw needs memory. It builds up context over time, using workspace files, conversation history, credentials, and configuration. If all of that lived inside the container, every rebuild would be a lobotomy. We’ll use volume mounts to keep OpenClaw’s memory persistent across rebuilds.

Host filesystem (persists)          Container (ephemeral)
─────────────────────────           ─────────────────────
~/.openclaw/                   →    /home/node/.openclaw/
  ├── config.json                     (workspace, memory, creds)
  ├── workspace/
  │   ├── SOUL.md
  │   ├── MEMORY.md
  │   └── memory/
  └── credentials/
~/container_local/             →    /home/node/.local/
  └── share/                          (signal-cli, claude cli)

Tear down the container, rebuild it, replace it entirely — OpenClaw’s knowledge and configuration persists.

Why Podman?

Docker’s rootless mode exists, but it was added after the fact — the Docker daemon was designed to run as root, and it shows. Rootless Docker still has limitations with AppArmor, cgroups, and networking that can bite you in production.

Podman was built rootless from the start. No daemon, no root requirement, each container can run as a user. Docker requires a background daemon (dockerd) running as root to manage containers — if it crashes, everything goes down, and dead containers can linger as zombies. Podman doesn’t have that. Each container is just a process. If it dies, it’s gone. This isn’t a knock on Docker — it’s a great tool and the right choice for a lot of workloads. But for running an AI agent with access to your personal data, Podman’s rootless-first design is a better fit.

Install Podman and Create Rootless User

Install Podman and Dependencies

1
sudo pacman -S podman podman-compose fuse-overlayfs slirp4netns

Packages: - podman - Container engine - podman-compose - Docker Compose compatibility - fuse-overlayfs - Efficient storage driver for rootless containers - slirp4netns - Rootless networking

Note: Podman defaults to pasta for rootless networking, but I’ve had better luck with slirp4netns.

Create a Dedicated Podman User

1
sudo useradd -m -s /bin/bash podman

No password on purpose — this is a service account, not a login account. Switch to it with sudo -iu podman when you need it.

Configure Subuid/Subgid Mappings

Rootless Podman needs subordinate UID/GID ranges for user namespace mapping:

echo "podman:100000:65536" | sudo tee -a /etc/subuid
echo "podman:100000:65536" | sudo tee -a /etc/subgid

Verify:

cat /etc/subuid
cat /etc/subgid

Enable Lingering

Allows containers to run without an active login session:

1
sudo loginctl enable-linger podman

Switch to the Podman User

1
sudo -iu podman

Verify Rootless Podman

1
2
3
podman --version
podman info --format '{{.Host.Security.Rootless}}'  # Should return "true"
podman run --rm hello-world

Set Up OpenClaw

Clone the Repository

Switch to the podman user first if you haven’t already:

sudo -iu podman
cd ~
git clone https://github.com/openclaw/openclaw.git
cd openclaw
git fetch --tags
git checkout $(git tag --sort=-v:refname | head -1)  # Latest stable release

Create Directories

1
2
mkdir -p ~/.openclaw/workspace ~/.openclaw/credentials
mkdir -p ~/container_local/share
  • ~/.openclaw/ - Main config directory (mounted to container)
  • ~/container_local/ - Container’s .local directory (for Claude CLI config, signal-cli data, etc.)

Create Environment File

Create ~/openclaw/.env:

1
2
3
4
5
6
OPENCLAW_CONFIG_DIR=/home/podman/.openclaw
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_BRIDGE_PORT=18790
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_GATEWAY_TOKEN=<generate-a-secure-token>
OPENCLAW_IMAGE=openclaw:local

Generate a secure token:

1
openssl rand -hex 32

Create the Management Script

Create ~/clawd.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash

COMPOSE_FILE="$HOME/openclaw/docker-compose.podman.yml"

case "$1" in
  start)
    podman-compose -f "$COMPOSE_FILE" up -d openclaw-gateway
    ;;
  stop)
    podman-compose -f "$COMPOSE_FILE" down
    ;;
  restart)
    podman-compose -f "$COMPOSE_FILE" down
    podman-compose -f "$COMPOSE_FILE" up -d openclaw-gateway
    ;;
  logs)
    podman logs -f openclaw_openclaw-gateway_1
    ;;
  status)
    podman ps -a --filter name=openclaw
    ;;
  build)
    podman-compose -f "$COMPOSE_FILE" build
    ;;
  cli)
    shift
    podman-compose -f "$COMPOSE_FILE" run --rm openclaw-cli "$@"
    ;;
  shell)
    podman exec -it openclaw_openclaw-gateway_1 bash
    ;;
  tui)
    podman exec -it openclaw_openclaw-gateway_1 node dist/index.js tui
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|logs|status|build|cli|shell|tui}"
    exit 1
    ;;
esac

Make it executable:

1
chmod +x ~/clawd.sh

Why a Custom Dockerfile?

OpenClaw’s default image is built on node:22-bookworm — a bare Node.js container. That’s fine for running the gateway itself, but the agent inside needs to do things: parse PDFs, process video, extract text from images, automate browsers, communicate over Signal. A stock Node.js container can’t do any of that.

Rather than exec-ing into the container as root every time you need a package (which defeats the point of rootless), we build a custom image with everything baked in. The tradeoff is a larger image (~1.5GB vs ~400MB), but you get a self-contained environment where OpenClaw has the tools it needs without runtime privilege escalation.

The full Dockerfile and docker-compose file are at the bottom of this post.

Build the Image

1
~/clawd.sh build

Start the Gateway

1
~/clawd.sh start

Verify:

1
2
3
~/clawd.sh status
ss -tlnp | grep 18789
~/clawd.sh logs

Managing the Container

Command Description
~/clawd.sh start Start the gateway
~/clawd.sh stop Stop the gateway
~/clawd.sh restart Restart the gateway
~/clawd.sh logs Follow container logs
~/clawd.sh status Show container status
~/clawd.sh build Rebuild the image
~/clawd.sh cli <args> Run OpenClaw CLI commands
~/clawd.sh shell Get a bash shell in the container
~/clawd.sh tui Launch the Terminal UI

Using OpenClaw

Web UI

Open in browser: http://localhost:18789

Terminal UI (TUI)

1
~/clawd.sh tui

Claude CLI

Get a shell and run Claude:

1
2
~/clawd.sh shell
claude

First run requires authentication setup.

OpenClaw CLI

1
2
3
~/clawd.sh cli --help
~/clawd.sh cli message send "hello"
~/clawd.sh cli doctor

Updating

1
2
3
4
5
cd ~/openclaw
git fetch --tags
git checkout $(git tag --sort=-v:refname | head -1)
~/clawd.sh build
~/clawd.sh restart

See available releases:

1
git tag --sort=-v:refname | head -10

File Locations

Path Description
~/.openclaw/ Main config directory
~/.openclaw/workspace/ Agent workspace (SOUL.md, IDENTITY.md, etc.)
~/.openclaw/credentials/ Stored credentials
~/.openclaw/config.json Gateway configuration
~/container_local/ Container’s .local directory
~/container_local/share/signal-cli/ Signal account data
~/container_local/share/claude/ Claude CLI config
~/openclaw/ Source repository
~/openclaw/.env Environment variables
~/openclaw/docker-compose.podman.yml Compose file
~/openclaw/Dockerfile.podman Custom Dockerfile
~/clawd.sh Management script

Troubleshooting

Permission denied on volumes

Fix ownership on host:

1
sudo chown -R podman:podman ~/.openclaw ~/container_local

Container exits immediately

Check logs:

1
~/clawd.sh logs

“No such device” error for /dev/net/tun

Expected in nested containers/VMs. Host networking is used instead.

npm/Claude CLI permission errors

The Dockerfile.podman fixes /home/node ownership. If issues persist:

1
podman exec -u root openclaw_openclaw-gateway_1 chown -R node:node /home/node

Configuration Files

docker-compose.podman.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Rootless Podman compose file for OpenClaw
# Uses host networking (TUN device not available for slirp4netns/pasta)
services:
  openclaw-gateway:
    build:
      context: .
      dockerfile: Dockerfile.podman
      network: host
    image: ${OPENCLAW_IMAGE:-openclaw:local}
    network_mode: host
    environment:
      HOME: /home/node
      TERM: xterm-256color
      OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
      CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
      CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
      CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
    volumes:
      - ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw:U
      - ${HOME}/container_local:/home/node/.local:U
    init: true
    restart: unless-stopped
    command:
      [
        "node",
        "dist/index.js",
        "gateway",
        "--bind",
        "${OPENCLAW_GATEWAY_BIND:-lan}",
        "--port",
        "${OPENCLAW_GATEWAY_PORT:-18789}",
        "--allow-unconfigured",
      ]

  openclaw-cli:
    image: ${OPENCLAW_IMAGE:-openclaw:local}
    network_mode: host
    environment:
      HOME: /home/node
      TERM: xterm-256color
      OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
      BROWSER: echo
      CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
      CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}
      CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE}
    volumes:
      - ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw:U
      - ${HOME}/container_local:/home/node/.local:U
    stdin_open: true
    tty: true
    init: true
    entrypoint: ["node", "dist/index.js"]

Dockerfile.podman

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
FROM node:22-bookworm

# Install Bun (required for build scripts)
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:${PATH}"

RUN corepack enable

WORKDIR /app

# Install Chromium, Java (for signal-cli), and system utilities
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
    chromium \
    xvfb \
    default-jre-headless \
    tesseract-ocr \
    poppler-utils \
    jq \
    ffmpeg \
    sqlite3 \
    pandoc \
    tree \
    zip \
    lynx \
    htop \
    && apt-get clean && \
    rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*

# Install Claude CLI globally
RUN npm install -g @anthropic-ai/claude-code

COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY patches ./patches
COPY scripts ./scripts

RUN pnpm install --frozen-lockfile

COPY . .

RUN OPENCLAW_A2UI_SKIP_MISSING=1 pnpm build
ENV OPENCLAW_PREFER_PNPM=1
RUN pnpm ui:build

ENV NODE_ENV=production

# Fix home directory ownership for node user
RUN chown -R node:node /app /home/node

USER node

CMD ["node", "dist/index.js"]

Packages included: - chromium — Browser automation for web scraping and interaction - xvfb — Virtual framebuffer for running Chromium in headed mode without a display - default-jre-headless — Java runtime for signal-cli - tesseract-ocr — OCR for image-based PDFs - poppler-utils — PDF text extraction (pdftotext) - jq — JSON parsing in shell - ffmpeg — Video/audio processing - sqlite3 — Database queries - pandoc — Document format conversion - tree — Directory visualization - zip — Archive creation - lynx — Text-based web browsing - htop — Process monitoring - Claude CLI — Anthropic’s CLI for Claude


Security Notes

  • Container runs as non-root user node (UID 1000)
  • OPENCLAW_GATEWAY_TOKEN protects the gateway API
  • Host networking exposes ports directly on host interface
  • Volumes use :U suffix for automatic UID mapping

Wrapping Up

At this point you’ve got OpenClaw running in an isolated, rootless container with its data safely persisted on the host. It can do its thing — run commands, parse files, browse the web — without having the keys to the rest of your machine.

From here, you can configure messaging channels (Signal, Telegram, Discord, etc.) through the OpenClaw CLI, set up skills and automations, and start customizing the agent’s workspace with your own memory files and tools. Check out the OpenClaw docs for the full rundown.

The beauty of this setup is that updates are painless: pull the latest release, rebuild, restart. Your agent’s brain survives every change. And if something ever goes sideways, you’re one build away from a clean slate.