OpenClaw is a personal AI assistant created by Peter Steinberger. It’s gone through a few name changes — Clawdbot, then Moltbot, now OpenClaw — but the idea has stayed the same: run your own AI agent on your own hardware, connected to messaging apps you already use. WhatsApp, Telegram, iMessage. No new app, no cloud dependency.

It’s powered by whatever LLM you want (Claude, GPT, Kimi), keeps persistent memory across conversations, and can actually do things: manage files, run terminal commands, browse the web. Most people run it on a Mac mini or spare laptop. I wanted something more lightweight and always-on, so I set it up on my Raspberry Pi 5 running Docker, accessible only through my WireGuard VPN.

The architecture

┌─────────────────────────────────────────────────────────────┐
│                      Raspberry Pi 5                          │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                 Docker Network (web)                 │    │
│  │  ┌─────────────┐         ┌─────────────────────┐    │    │
│  │  │   Caddy     │ ──────► │  OpenClaw Gateway   │    │    │
│  │  │  (HTTPS)    │         │  - Telegram Bot     │    │    │
│  │  │  :443       │         │  - WhatsApp         │    │
│  │  └─────────────┘         │  - Claude Code OAuth │   │    │
│  │                          │  :18789              │    │    │
│  │                          └─────────────────────┘    │    │
│  └─────────────────────────────────────────────────────┘    │
│                              │                               │
│                    WireGuard (wg0)                          │
│                       10.13.13.1                            │
└─────────────────────────────────────────────────────────────┘

                    ┌──────────┴──────────┐
                    │   WireGuard VPN     │
                    │   (encrypted)       │
                    └──────────┬──────────┘

                    ┌──────────┴──────────┐
                    │  Mac / Phone        │
                    │  10.13.13.x         │
                    └─────────────────────┘

The stack: Raspberry Pi 5 with Docker, Caddy as reverse proxy with self-signed HTTPS, WireGuard for secure remote access, and OpenClaw itself with Telegram and WhatsApp channels. For the LLM, you can use Kimi K2.5 (free) or Claude (paid). Brave Search is optional if you want web search capabilities.

Before you start, you’ll need Docker and Docker Compose installed, WireGuard configured, and a Telegram Bot Token from @BotFather. For the LLM, either grab a free API key from platform.moonshot.cn (Kimi) or set up Claude Code OAuth if you have a Claude subscription. A Brave Search API key is optional but nice to have.

Getting it running

The directory structure I ended up with:

/srv/openclaw/
├── docker-compose.yml
├── .env
├── config/                    # Mounted as /home/node/.openclaw
│   ├── openclaw.json
│   ├── credentials/
│   │   └── whatsapp/
│   └── agents/
│       └── main/
└── workspace/                 # Mounted as /home/node/clawd
    ├── USER.md
    ├── IDENTITY.md
    └── contacts.md

Start by creating it and cloning the source:

sudo mkdir -p /srv/openclaw
cd /srv/openclaw
mkdir -p config workspace
git clone https://github.com/openclaw/openclaw.git openclaw-src

The environment file

cat > .env << 'EOF'
OPENCLAW_GATEWAY_TOKEN=your-random-token-here
TELEGRAM_BOT_TOKEN=your-telegram-bot-token

# LLM Provider (choose one)
# Option A: Kimi Coding (free)
KIMI_API_KEY=your-kimi-api-key

# Option B: Claude Code OAuth (paid, requires Claude subscription)
# CLAUDE_CODE_OAUTH_TOKEN=your-claude-code-oauth-token

# Optional: Web search (get key from brave.com/search/api)
BRAVE_API_KEY=your-brave-api-key
EOF
VariableRequiredDescription
OPENCLAW_GATEWAY_TOKENYesRandom token for dashboard/API auth
TELEGRAM_BOT_TOKENYesFrom @BotFather on Telegram
KIMI_API_KEYOne LLM requiredFree API key from platform.moonshot.cn
CLAUDE_CODE_OAUTH_TOKENOne LLM requiredRequires Claude Pro subscription
BRAVE_API_KEYNoEnables web search. Free tier available at brave.com/search/api

Generate a random gateway token with openssl rand -hex 32. If you’re going the Claude route, run claude setup-token to get your OAuth token.

Docker Compose

services:
  openclaw-gateway:
    build:
      context: ./openclaw-src
      dockerfile: Dockerfile
    image: openclaw:local
    container_name: openclaw
    environment:
      - HOME=/home/node
      - TERM=xterm-256color
      - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
      - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
      # LLM: use one or both
      - KIMI_API_KEY=${KIMI_API_KEY}
      - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-}
      # Optional: web search
      - BRAVE_API_KEY=${BRAVE_API_KEY:-}
    volumes:
      - ./config:/home/node/.openclaw
      - ./workspace:/home/node/clawd
    ports:
      - "18789:18789"
    networks:
      - web
    init: true
    restart: unless-stopped
    command:
      - node
      - dist/index.js
      - gateway
      - --bind
      - lan
      - --port
      - "18789"

  openclaw-cli:
    image: openclaw:local
    container_name: openclaw-cli
    profiles:
      - cli
    environment:
      - HOME=/home/node
      - TERM=xterm-256color
      - OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
      - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
      - KIMI_API_KEY=${KIMI_API_KEY}
      - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-}
      - BRAVE_API_KEY=${BRAVE_API_KEY:-}
    volumes:
      - ./config:/home/node/.openclaw
      - ./workspace:/home/node/clawd
    networks:
      - web
    init: true

networks:
  web:
    external: true

Build and start with docker compose up -d --build. The first build takes a while on the Pi 5.

Initial setup

docker compose exec -it openclaw-gateway node dist/index.js onboard

The wizard asks you to pick your auth method, model, and channel. For auth, choose “Kimi Coding API key” for the free option or “Anthropic token (Claude Code)” if you’re paying for Claude. For the model, kimi-coding/k2p5 (Kimi K2.5) works well — 256k context window and reasoning capabilities, not bad for free. Select Telegram as the channel.

Verify it’s working:

docker compose logs | grep "agent model" | tail -1
# Should show: [gateway] agent model: kimi-coding/k2p5

Tip: Add google/gemini-2.0-flash as a fallback model in case Kimi hits rate limits on the free tier.

Then configure the gateway tokens:

docker compose exec openclaw-gateway node dist/index.js config set gateway.auth.token "your-gateway-token"
docker compose exec openclaw-gateway node dist/index.js config set gateway.remote.token "your-gateway-token"
docker compose restart openclaw-gateway

HTTPS with Caddy

OpenClaw’s dashboard requires HTTPS or localhost. Since this is behind WireGuard and not publicly accessible, I use Caddy with a self-signed certificate.

Add to your Caddyfile:

openclaw.yourdomain.local {
    tls internal
    reverse_proxy openclaw:18789
}

Add to your Mac’s /etc/hosts:

10.13.13.1 openclaw.yourdomain.local

Then access the dashboard at https://openclaw.yourdomain.local/?token=your-gateway-token. You’ll also need to enable insecure auth for self-signed certs:

docker compose exec openclaw-gateway node dist/index.js config set gateway.controlUi.allowInsecureAuth true
docker compose restart openclaw-gateway

Adding WhatsApp

This is where it gets interesting. Enable the channel, link your phone, and allowlist your number:

# Enable WhatsApp
docker compose exec openclaw-gateway node dist/index.js config set channels.whatsapp.enabled true

# Link your phone (scan the QR code with WhatsApp → Linked Devices)
docker compose exec -it openclaw-gateway node dist/index.js channels login --channel whatsapp

# Allowlist your number
docker compose exec openclaw-gateway node dist/index.js config set channels.whatsapp.allowFrom '["+your-phone-number"]'
docker compose restart openclaw-gateway

If you want to send WhatsApp messages from Telegram (cross-channel messaging):

docker compose exec openclaw-gateway node dist/index.js config set tools.message.crossContext.allowAcrossProviders true
docker compose restart openclaw-gateway

The contacts problem

On macOS, OpenClaw uses wacli to look up contacts by name. On the Pi, that’s not available. The workaround is simple — create a contacts file:

cat > /srv/openclaw/workspace/contacts.md << 'EOF'
# My Contacts

- Mom: +1234567890
- Dad: +0987654321
- Work: +1122334455
EOF

OpenClaw references this file when you ask to message someone by name. Not elegant, but it works.

Useful commands

# Check status
docker compose exec openclaw-gateway node dist/index.js status

# View logs
docker compose logs -f openclaw-gateway

# Send test message
docker compose exec openclaw-gateway node dist/index.js message send \
  --channel whatsapp --target "+1234567890" --message "test"

# Re-link WhatsApp
docker compose exec -it openclaw-gateway node dist/index.js channels login --channel whatsapp

# List skills
docker compose exec openclaw-gateway node dist/index.js skills list

# Install skills from ClawdHub
docker compose exec openclaw-gateway clawdhub search email
docker compose exec openclaw-gateway clawdhub install <skill-name>

Troubleshooting

”No API key found for provider anthropic”

The agent auth isn’t configured. Make sure your LLM API key is in both the .env file and the docker-compose.yml environment section. Run the configure wizard again if needed:

docker compose run --rm openclaw-cli node dist/index.js configure

WhatsApp says “No active listener”

Restart the gateway after linking:

docker compose restart openclaw-gateway

“unauthorized: gateway token mismatch”

Sync the tokens:

source /srv/openclaw/.env
docker compose exec openclaw-gateway node dist/index.js config set gateway.auth.token "$OPENCLAW_GATEWAY_TOKEN"
docker compose exec openclaw-gateway node dist/index.js config set gateway.remote.token "$OPENCLAW_GATEWAY_TOKEN"
docker compose restart openclaw-gateway

Volume mount confusion

OpenClaw has gone through a few name changes (Clawdbot → Moltbot → OpenClaw), so you might see references to .clawdbot or .moltbot in older docs. Use /home/node/.openclaw for the config mount. Migration warnings can usually be ignored.

Security

The setup is intentionally locked down:

  • The Pi is only reachable via WireGuard VPN
  • Caddy provides HTTPS (self-signed, but encrypted)
  • The gateway token is required for dashboard and CLI access
  • API keys stay in the .env file
  • Only port 443 (Caddy) and WireGuard are exposed externally

On my radar: nanobot

While OpenClaw works well, I’ve been eyeing nanobot as an alternative. It bills itself as “The Ultra-Lightweight Clawdbot” — about 4,000 lines of Python compared to OpenClaw’s 430k+ lines. For a Raspberry Pi setup, that’s appealing: faster startup, lower memory usage, pip install nanobot-ai and you’re done. It supports Telegram and WhatsApp through OpenRouter, and the codebase is clean enough to hack on.

The tradeoff is fewer features — no ClawdHub skills ecosystem, simpler memory system, less polish. But for a personal AI assistant that handles messages and runs basic tasks, it might be all I need. I’m planning to spin up a second container and run them side-by-side for a while.