# OpenClaw on TrueNAS SCALE Self-hosted AI agent gateway that connects LLMs to messaging platforms (Telegram, Discord, WhatsApp). Runs as a persistent daemon — can message proactively, execute shell commands, manage files, and automate tasks. - Previously known as: ClawdBot, MoltBot - GitHub: https://github.com/openclaw/openclaw - Docs: https://docs.openclaw.ai ## Prerequisites - TrueNAS SCALE (24.10 Electric Eel or newer recommended — native Docker support) - Dockge running on TrueNAS for Docker Compose management - A dataset for OpenClaw storage: `tank/configs/openclaw` - NanoGPT API key from https://nano-gpt.com (or OpenRouter key from https://openrouter.ai) - Telegram bot token from @BotFather ## 1. Create Storage Datasets In TrueNAS web UI, create two datasets under your apps pool: ``` tank/configs/openclaw/config # maps to ~/.openclaw tank/configs/openclaw/workspace # maps to ~/openclaw/workspace ``` Set permissions to UID 1000 (the container runs as `node` uid 1000): ```bash sudo chown -R 1000:1000 /mnt/tank/configs/openclaw/config sudo chown -R 1000:1000 /mnt/tank/configs/openclaw/workspace ``` > **Important:** If you skip this step, onboarding will fail with `EACCES: permission denied` when writing `openclaw.json`. ## 2. Create the Telegram Bot 1. Open Telegram, search for `@BotFather` 2. Send `/newbot` 3. Choose a name and username (username must end in `bot`) 4. Save the bot token (format: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`) ## 3. Deploy via Dockge In Dockge, create a new stack called `openclaw`. ### Compose YAML Two services are required: `openclaw-gateway` runs the persistent gateway, and `openclaw-cli` is used for one-off CLI commands (onboarding, channel setup, diagnostics). The `cli` profile prevents it from starting automatically. ```yaml services: openclaw-gateway: image: ghcr.io/openclaw/openclaw:latest container_name: openclaw restart: unless-stopped ports: - "18789:18789" volumes: - /mnt/tank/configs/openclaw/config:/home/node/.openclaw - /mnt/tank/configs/openclaw/workspace:/home/node/workspace environment: - NANO_GPT_API_KEY=${NANO_GPT_API_KEY} - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} command: ["node", "openclaw.mjs", "gateway", "--allow-unconfigured", "--bind", "lan"] openclaw-cli: image: ghcr.io/openclaw/openclaw:latest volumes: - /mnt/tank/configs/openclaw/config:/home/node/.openclaw - /mnt/tank/configs/openclaw/workspace:/home/node/workspace environment: - NANO_GPT_API_KEY=${NANO_GPT_API_KEY} - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} entrypoint: ["node", "openclaw.mjs"] profiles: - cli ``` > **Why two services?** The Docker image's default entrypoint runs `node openclaw.mjs gateway`. If you try to run CLI commands (like `onboard`) via the gateway service, it fails with `Cannot find module '/app/onboard'` because the entrypoint tries to execute the argument as a standalone script. The `openclaw-cli` service overrides `entrypoint` to `["node", "openclaw.mjs"]` so subcommands work correctly. > **Why `--bind lan`?** The gateway defaults to binding on `127.0.0.1` (loopback) inside the container. Docker port mapping requires the process to listen on `0.0.0.0`, so `--bind lan` is mandatory for the port forwarding to work. ### Environment Variables In the Dockge `.env` section, add: ``` NANO_GPT_API_KEY=your-nanogpt-key-here OPENROUTER_API_KEY=your-openrouter-key-here ``` ### First Run Before starting the stack normally, run the onboarding wizard via SSH: ```bash cd /mnt/tank/stacks/openclaw sudo docker compose run --rm openclaw-cli onboard --no-install-daemon ``` The `--no-install-daemon` flag is required in Docker since the gateway runs as a separate container, not as a system daemon. After onboarding completes, start the stack from the Dockge UI (or `sudo docker compose up -d openclaw-gateway`). ## 4. Configure OpenClaw After the container is running, the config file lives at: ``` /mnt/tank/configs/openclaw/config/openclaw.json ``` The onboarding wizard writes this file. Key settings to verify: - `gateway.bind` should be `"lan"` (not `"loopback"`) - `gateway.auth.mode` should be `"token"` with a generated token - `channels.telegram.enabled` should be `true` ### NanoGPT as Provider (OpenAI-compatible) NanoGPT exposes an OpenAI-compatible API at `https://nano-gpt.com/api/v1`. Configure it as a custom provider: ```json5 { "agents": { "defaults": { "model": { "primary": "nanogpt/claude-sonnet-4.5", "fallbacks": ["openrouter/anthropic/claude-sonnet-4.5"] } } }, "env": { "NANO_GPT_API_KEY": "your-nanogpt-key", "OPENROUTER_API_KEY": "sk-or-your-openrouter-key" } } ``` > **Note:** If NanoGPT is not natively supported as a provider, configure it as a custom provider with base URL `https://nano-gpt.com/api/v1`. Check the [custom providers docs](https://docs.openclaw.ai/gateway/configuration-reference#custom-providers-and-base-urls) for exact syntax. ### OpenRouter as Provider (alternative) ```json5 { "agents": { "defaults": { "model": { "primary": "openrouter/anthropic/claude-sonnet-4.5" } } }, "env": { "OPENROUTER_API_KEY": "sk-or-your-key" } } ``` ## 5. Connect Telegram From SSH on TrueNAS, use the CLI service: ```bash cd /mnt/tank/stacks/openclaw sudo docker compose run --rm openclaw-cli channels add --channel telegram --token "YOUR_BOT_TOKEN" ``` Then approve the pairing. Send a message to your bot in Telegram — it will reply with a pairing code. Approve it: ```bash sudo docker compose run --rm openclaw-cli pairing approve telegram ``` ## 6. Access the Control UI The Control UI requires HTTPS or localhost (browser secure context requirement). Direct access via LAN IP will not work. ### Step 1: SSH tunnel ```bash ssh -L 18789:localhost:18789 truenas_admin@192.168.178.189 ``` ### Step 2: Get the dashboard URL with token The gateway uses token-based authentication. The token is embedded in the URL fragment (`#token=...`). Get the full URL: ```bash cd /mnt/tank/stacks/openclaw sudo docker compose run --rm openclaw-cli dashboard --no-open ``` This prints a URL like: ``` http://172.16.10.3:18789/#token=your-gateway-token-here ``` ### Step 3: Open in browser Replace the IP with `localhost` and open: ``` http://localhost:18789/#token=your-gateway-token-here ``` > **Do not** access via `http://192.168.178.189:18789` directly — the gateway rejects non-localhost, non-HTTPS WebSocket connections with `code=4008 reason=connect failed`. ## 7. Verify - Check gateway status: `sudo docker compose run --rm openclaw-cli gateway status` - View logs: `sudo docker logs -f openclaw` - Run diagnostics: `sudo docker compose run --rm openclaw-cli doctor` ## 8. Security Considerations - **Do not expose port 18789 to the public internet.** Use Tailscale, WireGuard, or VPN to access the Control UI remotely. - Enable **explicit consent mode** to require approval before OpenClaw executes write/exec commands. - Treat the `/mnt/tank/configs/openclaw/config` directory as sensitive — it contains API keys and session data. - The container runs as non-root (uid 1000), which is good practice. - Consider network isolation: create a dedicated Docker network or VLAN if your TrueNAS hosts other services. ## Useful Commands All CLI commands below assume you are in `/mnt/tank/stacks/openclaw`. | Command | Description | |---------|-------------| | `sudo docker compose run --rm openclaw-cli gateway status` | Check if gateway is running | | `sudo docker compose restart openclaw-gateway` | Restart the gateway | | `sudo docker compose run --rm openclaw-cli doctor` | Automated health checks | | `sudo docker compose run --rm openclaw-cli dashboard --no-open` | Get Control UI URL with token | | `sudo docker logs -f openclaw` | Stream live logs | | `sudo docker compose run --rm openclaw-cli channels list` | List connected channels | | `sudo docker compose pull && sudo docker compose up -d openclaw-gateway` | Update to latest version | ## Troubleshooting - **`Cannot find module '/app/onboard'`**: You ran a CLI subcommand via the gateway service. Use `openclaw-cli` service instead (it has the correct entrypoint). - **`EACCES: permission denied` on config files**: Host directories not owned by UID 1000. Run `sudo chown -R 1000:1000 /mnt/tank/configs/openclaw/config`. - **`Bind: loopback` in doctor output**: The onboarding wizard set bind to loopback in the config file, overriding the `--bind lan` command flag. Edit `openclaw.json` and set `gateway.bind` to `"lan"`, then restart the gateway. - **Browser shows "connection reset" or blank page**: You are accessing via LAN IP instead of localhost. Use the SSH tunnel and open `http://localhost:18789/#token=...`. - **`code=4008 reason=connect failed` in logs**: The browser is connecting without the required token or via a non-secure origin. Get the full URL with `dashboard --no-open`. - **Gateway Bridge errors**: Try `network_mode: host` in the compose file if bridge networking causes issues. - **Container won't start**: Check `sudo docker logs openclaw` for config validation errors — OpenClaw rejects malformed JSON5.