OpenClaw Runbook

Chapter 5 of 8 · 14 min read

VPS Deployment

A full Hetzner setup with Tailscale SSH, firewall hardening, gateway auth, openclaw backup, systemd service management and production stability patterns.

01Running on a VPS #

Running OpenClaw on a VPS doesn't need a large machine. A Hetzner CX23 is plenty.

Put Tailscale on both your local machine and the VPS. Install it on the VPS with the --ssh=true flag and you can log in over Tailscale directly. Then block port 22 entirely in the Hetzner firewall.

Block all inbound traffic and reach everything over Tailscale. The Gateway itself stays on loopback; you reach its dashboard through an SSH tunnel or Tailscale Serve. A simple setup, and one less thing to worry about.

02Hardware requirements #

Minimum specs: a Hetzner CX23 or equivalent. That is enough for a native install; building the official Docker image from source needs a server with at least 6 GB of RAM.

vCPUs
2
RAM
4 GB
Disk
40 GB
Monthly cost
~$5/mo

03Network security with Tailscale #

Step 1: Install Tailscale on your local machine

Local machine
# On your local machine (Mac/Linux/Windows)
# Visit: https://tailscale.com/download
# Install and authenticate

Step 2: Install Tailscale on the VPS

VPS
# SSH into the VPS normally first
ssh user@your-vps-ip

# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh=true

The --ssh=true flag enables SSH over Tailscale.

Step 3: Get the VPS Tailscale IP

VPS
# On the VPS, get your Tailscale IP
tailscale ip -4
# Example output: 100.64.1.2

Step 4: Test SSH via Tailscale

Local machine
# Use the Tailscale IP, NOT your public IP
ssh user@100.64.1.2

Step 5: Block public access, only after verification

Hetzner firewall (web UI)
# 1. Create a new firewall rule
# 2. Block: all inbound traffic on port 22
# 3. Leave Tailscale traffic unrestricted (100.64.0.0/10)

Step 6: Verify lockout prevention

Verification
# This should work (Tailscale)
ssh user@100.64.1.2

# This should fail/timeout (public IP blocked)
ssh user@your-public-vps-ip

04OpenClaw installation #

The installer script sets up a supported Node runtime (24.16+ or 26.1+; Node 22 is no longer supported) and installs OpenClaw. Onboarding then writes your config and installs the Gateway as a systemd user service.

VPS
# Installs Node if needed (24.16+ or 26.1+), then OpenClaw
curl -fsSL https://openclaw.ai/install.sh | bash

# Onboard and install the systemd user service
openclaw onboard --install-daemon

Then run these before doing anything else:

bash
# Health checks, config migrations and guided repairs
openclaw doctor --fix

# Security audit, including a live Gateway probe
openclaw security audit --deep

The security audit should return zero critical issues. Warnings you may see:

  • fs.credentials_dir.perms_readable: run openclaw security audit --fix, which tightens state and config permissions
  • gateway.trusted_proxies_missing: only fires when reverse-proxy headers reach the Gateway. Behind nginx or Caddy, set gateway.trustedProxies

05File permissions #

Lock down the config directory:

bash
chmod 700 ~/.openclaw
chmod 600 ~/.openclaw/openclaw.json
chmod 700 ~/.openclaw/credentials

This stops other users on the system from reading your config, credentials and the SQLite databases that hold auth profiles and transcripts. openclaw security audit --fix applies the same 700/600 permissions for you.

06Gateway binding and auth #

Verify the gateway binds to localhost and isn't exposed to the public internet:

bash
ss -ltn | grep 18789
# You want to see: 127.0.0.1:18789
# You do NOT want: 0.0.0.0:18789

Correct

127.0.0.1:18789

Only localhost can reach the gateway

Dangerous

0.0.0.0:18789

Anyone who can reach port 18789 can talk to your agent

Loopback is the default. Gateway auth is required too: with no token or password the Gateway refuses WebSocket connections, and startup rejects blank or placeholder secrets. Onboarding generates a token; openclaw doctor --generate-gateway-token makes a new one.

openclaw.json
"gateway": {
  "mode": "local",
  "bind": "loopback",
  "port": 18789,
  "auth": { "mode": "token", "token": "<openssl rand -hex 32>" }
}

To use the dashboard from your laptop, let OpenClaw run Tailscale Serve. The Gateway stays on 127.0.0.1 and Tailscale provides tailnet-only HTTPS:

openclaw.json
"gateway": {
  "bind": "loopback",
  "tailscale": { "mode": "serve" }
}

07Hardening configuration #

Logging and redaction

Redaction of log tails, console output and diagnostic exports is always on and no longer configurable; openclaw doctor --fix removes the retired redactSensitive switch. Add your own patterns for hostnames, internal URLs or custom token formats:

openclaw.json
"logging": {
  "redactPatterns": ["\\bTOKEN\\b\\s*[=:]\\s*(\\S+)"]
}

The rolling files in /tmp/openclaw/ are raw (owner-only 0600), and tool arguments, tool results and transcripts are never masked. Treat them as secrets, and share openclaw status --all instead of raw logs.

Tool policies

Restrict which tools agents can use. A profile sets the base list; alsoAllow adds to it, and deny always wins:

openclaw.json
"tools": {
  "profile": "minimal",
  "alsoAllow": ["read", "web_search", "web_fetch"]
}
Tool profiles
minimalsession_status, plus gateway for updates only
codingFiles, exec, web, sessions, memory, cron, media
messagingThe message tool, session tools and status
fullNo profile filtering (what onboarding sets)

Sandbox mode (optional)

For containerized tool execution (requires Docker). Sandboxing is off by default; non-main sandboxes every session except your main one:

openclaw.json
"agents": {
  "defaults": {
    "sandbox": {
      "mode": "non-main",
      "scope": "agent",
      "workspaceAccess": "none",
      "docker": { "image": "openclaw-sandbox:bookworm-slim" }
    }
  }
}

Build the openclaw-sandbox:bookworm-slim image first. OpenClaw fails fast rather than falling back to a plain Debian image. Useful on a shared VPS where you want agent work isolated.

08Git-track your config #

Git-track the OpenClaw config directory so you can roll back:

bash · initial setup
cd ~/.openclaw && git init
# Track only the config; state lives in SQLite databases
printf '*\n!.gitignore\n!openclaw.json\n' > .gitignore
git add .gitignore openclaw.json
git commit -m "config: baseline"
bash · before and after changes
# Before making changes
git commit -am "config: before model update"

# Make your changes
vim openclaw.json

# Validate and commit
openclaw config validate
git commit -am "config: switch heartbeat model"

When something breaks at midnight, git diff and git checkout are faster than trying to remember what you changed. Most config edits hot-reload; auth-mode changes need a Gateway restart.

09Validation workflow #

After any config change:

  1. 1

    Validate

    openclaw config validate

  2. 2

    Health check

    openclaw doctor

  3. 3

    Security audit

    openclaw security audit --deep

  4. 4

    Test

    openclaw status, then send a test message

  5. 5

    Commit

    git commit -am "config: description of change"

10System monitoring #

bash
# Check OpenClaw status
openclaw status

# Service state and readiness
openclaw gateway status --deep

# Check for errors in recent logs
openclaw logs --plain --limit 500 | grep -i error

# Check system resources
htop

Consider a cron job or heartbeat check for failed jobs and error logs. See the Heartbeat & Task Tracking chapter.

11Backup strategy #

Back up

  • ~/.openclaw/ (config, credentials, SQLite state)
  • Agent directories
  • Workspaces

Never

  • Raw copies of live .sqlite, -wal or -shm files
  • Rolling logs in /tmp/openclaw/
  • Sandbox runtimes (recreated)

State lives in SQLite databases that the Gateway writes continuously, so a plain tar of ~/.openclaw can capture a torn database. openclaw backup snapshots them safely while the Gateway runs.

Backup script

Create ~/bin/backup-openclaw.sh:

backup-openclaw.sh
#!/bin/bash
# OpenClaw backup script

BACKUP_DIR=~/backups
mkdir -p $BACKUP_DIR

# Archive state, config, credentials, agents and workspaces.
# SQLite databases are captured safely while the Gateway runs.
openclaw backup create --output $BACKUP_DIR --verify

# Keep only the last 7 days of archives
find $BACKUP_DIR -name "*-openclaw-backup.tar.gz" -mtime +7 -delete

echo "$(date): backup completed" >> $BACKUP_DIR/backup.log
bash · cron schedules
chmod +x ~/bin/backup-openclaw.sh
crontab -e

# Daily at 3 AM
0 3 * * * ~/bin/backup-openclaw.sh

# Every 6 hours
0 */6 * * * ~/bin/backup-openclaw.sh

# Weekly on Sunday at 2 AM
0 2 * * 0 ~/bin/backup-openclaw.sh

Or let the Gateway schedule it

With a storage location configured (a disk or object store), the Gateway can create, verify, upload and prune backups itself:

bash
# Gateway-owned daily backups to a configured storage location
openclaw backup enable --to offsite --every 24h --keep-daily 7 --keep-weekly 4

# Restore stages files into a fresh directory
openclaw backup restore <archive.tar.gz> --target ./restored-openclaw

Archives contain credentials and session history. Store them encrypted, and restrict the destination like the live state directory.

12Service management #

openclaw onboard --install-daemon installs a systemd user unit, openclaw-gateway.service, on Linux (launchd on macOS). On a headless VPS, enable lingering so the service survives logout.

bash
# Check service status
openclaw gateway status
# or
systemctl --user status openclaw-gateway

# Manage the service
openclaw gateway start
openclaw gateway stop
openclaw gateway restart

# Keep the user service running after you log out
sudo loginctl enable-linger $(whoami)

13Get stable before going 24/7 #

Don't run OpenClaw 24/7 until the basics are ironed out. Get your config right, get your agents stable, get your costs predictable. Then let it run.

People who jump to 24/7 operation on day one end up with a bot that burns $50 in API calls overnight because a retry loop went sideways.

14Troubleshooting #

Tailscale SSH not working

bash
sudo tailscale status
sudo tailscale status | grep ssh
sudo tailscale up --ssh=true

Can't find the Tailscale IP

bash
tailscale ip -4    # IPv4
tailscale ip -6    # IPv6
hostname           # Machine name on the Tailscale network

Gateway won't start

bash
openclaw gateway status --deep
openclaw doctor --fix
openclaw gateway restart

Port already in use

bash
ss -ltnp | grep 18789
# Stop the other process or change gateway.port

Locked out after a firewall change

  1. Use the provider's web console (Hetzner Console, DigitalOcean Droplet Console).
  2. Log in through the web interface.
  3. Fix the firewall rules or re-enable Tailscale.
  4. Test Tailscale SSH before closing the console.

15Pre-production security checklist #

Before running OpenClaw in production:

  • File permissions locked down (700/600)
  • Gateway binds to loopback with a long random auth token
  • openclaw security audit --deep returns zero critical issues
  • Tailscale configured, SSH blocked at the firewall
  • DMs on pairing or allowlists, groups behind a mention
  • Tool policies configured
  • Config git-tracked, never pushed with tokens in it
  • Backups run with openclaw backup and are verified

16Keeping the VPS cheap #

Running on a VPS doesn't have to be expensive:

  • Hetzner CX23: ~$5/month
  • A cheap, small model for heartbeats
  • Cross-provider fallbacks to avoid quota burnout
  • Concurrency limits to prevent runaway costs

See the Agent Prompts chapter for model configuration strategies.

Adapted from digitalknk/openclaw-runbook. OpenClaw changes fast: check the official docs before copying config into production.