Secure Self-Hosted OpenClaw AI Assistant: Step-by-Step Proxmox Template Guide with Tailscale Integration
For the last few weeks, you could not escape seeing "Clawd" everywhere - the viral AI assistant that went from zero to 100,000 GitHub stars in just two months. Originally called Clawdbot, now rebranded as OpenClaw, it's an open-source AI that actually does things instead of just chatting. So this weekend, I set it up on my Proxmox homelab.
Now I have a digital assistant that I can communicate with through WhatsApp and Telegram. I asked it to choose its own name and persona, and now it is called Lyra. I gave it a dedicated email and GitHub account.
This guide walks through creating a reusable Proxmox template with all dependencies pre-installed. Clone the template, boot it, configure OpenClaw and Tailscale, and you're done.
What you get:
- Debian 13 VM template with Node.js 24, OpenClaw CLI, and Tailscale pre-installed
- Automatic installation via cloud-init (no manual package installs)
- Secure HTTPS access via Tailscale Serve (no public exposure)
- Progress indicators and status helpers baked in
- ~15 minutes from template creation to running AI assistant
GitHub: OpenClaw Proxmox Template Script
What is OpenClaw?
OpenClaw is an autonomous AI assistant that actually executes tasks:
- Controls browsers (fill forms, scrape data, automate workflows)
- Manages files on your system
- Integrates with WhatsApp, Telegram, Discord
- Runs terminal commands
- All through a web UI or chat interfaces
Think of it as the plumbing layer that connects AI models (Claude, GPT-4o, Gemini) to real-world actions.
The catch: It's powerful, which means it needs proper isolation. Hence the dedicated VM approach.
Architecture
Your Browser
│
│ HTTPS (Tailscale Serve)
▼
OpenClaw VM (Proxmox)
│
├─ Tailscale Serve → http://127.0.0.1:18789
│
├─ OpenClaw Gateway
│ ├─ Control UI
│ └─ AI agent + channels
│
└─ Internet (outbound only)
└─ WhatsApp/Telegram/AI APIs
Key design:
- Gateway binds to loopback (127.0.0.1) only
- Tailscale Serve proxies HTTPS from your Tailnet
- No public exposure, no firewall rules needed
- Messaging platforms connect over regular internet
- If using Tailscale, the server is only accessible through your Tailscale network, ensuring secure, private access
Prerequisites
You'll need:
- Proxmox VE 8.x (ensure it's updated:
apt update && apt full-upgradevia SSH or web console) - At least 4GB RAM and 20GB storage available per VM (adjust based on workload)
- SSH access to Proxmox (or use the web console as a fallback)
- A Tailscale account (free tier works)
- Your SSH public key
- Cloud-init enabled in Proxmox (check under Datacenter > Options; if not, enable it)
Quick Tip: If you're new to Tailscale, start with their quickstart guide: Tailscale Docs.
Part 1: Create the Template
Download the Script
SSH into Proxmox and download the script directly to avoid copy-paste errors:
ssh root@your-proxmox-host
wget https://raw.githubusercontent.com/Ali-Shaikh/proxmox-toolbox/main/templates/openclaw/debian-13-openclaw-ready-template.sh -O debian-13-openclaw-ready-template.sh
Configure It
Open the script and modify these values:
nano debian-13-openclaw-ready-template.sh
Change these:
VMID=10010 # Template ID (pick any unused ID; check with `qm list`)
STORAGE="local-lvm" # Your Proxmox storage pool (verify with `pvesm list`)
MEMORY=4096 # 4GB RAM minimum
CORES=2 # CPU cores
CI_USER="debian"
CI_PASSWORD="$(openssl passwd -6 $(pwgen -s 16 1))" # Generate a strong password with pwgen or similar; install pwgen if needed: apt install pwgen
# Replace with YOUR SSH public key
echo "ssh-rsa AAAA... your-email@example.com" > ~/ssh.pub
Note: Homebrew (Linuxbrew) is installed via cloud-init for certain dependencies; it's not native to Debian but works fine for this setup.
Run It
chmod +x debian-13-openclaw-ready-template.sh
./debian-13-openclaw-ready-template.sh
The script will:
- Download Debian 13 cloud image
- Create a VM with UEFI support
- Configure cloud-init to install Node.js, OpenClaw, Tailscale, pnpm, Homebrew
- Convert it to a template
Time: ~2-3 minutes
Part 2: Deploy a VM
Clone the template (replace IDs if needed; check for conflicts with qm list):
qm clone 10010 201 --name openclaw-prod --full
qm start 201
Wait 5-7 minutes. Cloud-init is installing everything. You can watch progress:
# Get the VM's IP (once it boots)
qm guest cmd 201 network-get-interfaces
# SSH in and watch the install
ssh debian@<vm-ip>
sudo tail -f /var/log/openclaw-install.log
When you see "Installation Complete", you're ready.
Verify Installation
# Quick check
check-install-status
# Or check versions manually
cat /root/install-versions.txt
You should see Node.js 24, npm, pnpm, OpenClaw, Tailscale, and Homebrew all installed.
Part 3: Configure OpenClaw
Connect to Tailscale
Get an auth key from https://login.tailscale.com/admin/settings/keys (set to expire in 30-90 days for security).
sudo tailscale up --authkey=tskey-auth-YOUR_KEY_HERE
tailscale status # Verify connection
Set Up pnpm (Required for Skills)
pnpm setup
source ~/.bashrc
Run OpenClaw Onboarding
openclaw onboard --install-daemon
Critical configuration choices:
| Question | Answer | Why? |
|---|---|---|
| Setup type | Local gateway (this machine) | Keeps everything self-contained. |
| Bind address | Loopback (127.0.0.1) - MUST BE LOOPBACK | Prevents direct exposure to LAN or internet; required for secure Tailscale proxying. |
| Authentication | Token (Recommended) | Stronger than alternatives for access control. |
| Expose via | Tailscale Serve | Secure Tailnet-only access without public exposure. |
Important:
- Never select "LAN (0.0.0.0)" for bind address
- Do not select "Funnel" (exposes to public internet)
- If you get a Tailscale binary warning, choose "No" and continue
Save Your Token
The wizard will display your gateway token. Save it:
cat ~/.openclaw/openclaw.json | jq -r '.gateway.auth.token'
Output: claw_abc123def456...
Write this down. You'll need it to access the UI.
Verify Tailscale Serve
tailscale serve status
Expected output:
https://openclaw-host.tail-scale.ts.net/ (Tailscale Serve)
|-- / proxy http://127.0.0.1:18789
If empty, configure manually:
sudo tailscale serve --bg --https=443 http://127.0.0.1:18789
Check Gateway Status
openclaw status
openclaw health
openclaw gateway logs # Watch for errors
Part 4: Access the UI
From any device on your Tailscale network:
# Get all access info in one command
openclaw-access-info
This shows your Tailscale URL and gateway token. Open the URL in your browser.
Example: https://openclaw-host.tail-scale.ts.net/
Enter your gateway token when prompted. If "allowTailscale": true is enabled in your config, you'll be auto-authenticated - see Configuration File for trade-offs.
Important: Based on common troubleshooting (though not explicitly in OpenClaw docs), when using Tailscale Serve, you may need to add "controlUi": { "allowInsecureAuth": true } to your config file to avoid "pairing required" WebSocket errors. This skips device pairing for compatibility but reduces security slightly; use only if needed and combine with strong token auth.
Configuration File
Your OpenClaw config lives at ~/.openclaw/openclaw.json:
{
"gateway": {
"port": 18789,
"bind": "127.0.0.1",
"tailscale": {
"mode": "serve"
},
"auth": {
"mode": "token",
"token": "your-gateway-token-here",
"allowTailscale": false // Set to true for auto-auth via Tailscale identity (convenience trade-off: trust your Tailnet fully or use token-only for stricter security)
},
"controlUi": {
"allowInsecureAuth": true // May be needed for Tailscale Serve to fix WebSocket errors; skips device pairing (use with caution)
}
}
}
Key settings:
bind: "127.0.0.1"- Gateway only listens on loopback (required for Tailscale Serve)tailscale.mode: "serve"- Tailnet-only accessauth.allowTailscale: false- Prefer token auth; enabletrueonly if you fully trust your Tailnet (trade-off: easier access vs. potential risk if Tailnet is compromised)controlUi.allowInsecureAuth: true- May be needed for Tailscale Serve - Skips device pairing to prevent "pairing required" WebSocket errors (trade-off: fixes compatibility but weakens pairing-based security; not explicitly required in OpenClaw docs but helps with common issues)
View your config:
cat ~/.openclaw/openclaw.json | jq
Add Chat Integrations
openclaw channels login
# Scan QR code with WhatsApp > Settings > Linked Devices (note: WhatsApp limits linked devices; check your account limits)
Telegram
openclaw configure --section channels.telegram
# Add bot token from @BotFather
Discord
openclaw configure --section channels.discord
# Add bot token from Discord Developer Portal
Note: For LLM integrations (e.g., Claude, GPT-4o keys), add them securely via openclaw configure --section models. Store keys in environment variables or a secrets manager for best practices.
Troubleshooting
Tailscale Serve Not Working
Check Tailscale status:
tailscale status
sudo systemctl status tailscaled
Manually configure Serve:
sudo tailscale serve --bg --https=443 http://127.0.0.1:18789
tailscale serve status
Ensure HTTPS is enabled for your Tailnet:
- Visit https://login.tailscale.com/admin/dns
- Enable HTTPS if prompted
Can't Access from Remote Device
Verify both devices are on Tailscale:
tailscale status # Run on both devices
Test connectivity:
ping openclaw-host
curl -k https://openclaw-host.tail-scale.ts.net/health
WebSocket Errors: "pairing required" (1008)
Edit your config:
nano ~/.openclaw/openclaw.json
Add under the gateway section:
"controlUi": {
"allowInsecureAuth": true
}
Restart the gateway:
systemctl --user restart openclaw-gateway
This skips device pairing for Tailscale compatibility.
Installation Seems Stuck
# Check cloud-init progress
sudo tail -100 /var/log/cloud-init-output.log
# Check OpenClaw install progress
sudo tail -100 /var/log/openclaw-install.log
# Current step
cat /var/run/openclaw-install-progress
pnpm Global Bin Error
If you see ERR_PNPM_NO_GLOBAL_BIN_DIR when installing skills:
pnpm setup
source ~/.bashrc
openclaw onboard --install-daemon # Retry
Proxmox-Specific Issues
- VM won't boot: Check UEFI settings in Proxmox VM hardware tab; ensure BIOS is set to OVMF (UEFI).
- No IP assigned: Verify DHCP in your Proxmox network bridge; restart VM if needed.
Reset Everything
Start over if needed:
openclaw gateway stop
rm -rf ~/.openclaw/
openclaw onboard --install-daemon
Diagnostic Commands
openclaw doctor # Comprehensive health check
openclaw status --all # Deep status
openclaw health # Quick health probe
openclaw gateway logs -f # Follow gateway logs
Security Considerations
OpenClaw can execute commands and access your system. Here's how to secure it:
Gateway Security
1. Loopback binding only
{ "gateway": { "bind": "127.0.0.1" } }
2. Token authentication
{
"gateway": {
"auth": {
"mode": "token",
"allowTailscale": false // Token-only is stricter; enable true only if Tailnet is highly trusted
}
}
}
3. Rotate tokens regularly (every 90 days)
openclaw configure --section gateway.auth
4. Monitor access logs
openclaw gateway logs | grep -i "error\|unauthorised\|failed"
Tailscale Security
- Use Serve (Tailnet-only), not Funnel (public).
- Generate auth keys with 30-90 day expiration; rotate them.
- Enable firewall (e.g., UFW:
sudo ufw enableand allow only necessary outbound traffic).
System Security
Regular updates:
sudo apt update && sudo apt upgrade -y
openclaw update
Consider automating via cron: crontab -e and add 0 2 * * * /usr/bin/apt update && /usr/bin/apt upgrade -y.
Backups:
Take Proxmox snapshots before changes: qm snapshot 201 pre-config --description 'Before updates'.
Also, backup config:
tar -czf openclaw-backup-$(date +%Y%m%d).tar.gz ~/.openclaw/
Best Practices:
- Use token authentication
- Enable Tailscale Serve (not Funnel)
- Regular backups and updates
- Monitor logs for issues
- Rotate credentials periodically
- Never expose gateway on 0.0.0.0
- Never commit tokens to git
- Install monitoring tools like Fail2Ban for SSH:
sudo apt install fail2ban - For least privilege (e.g., running OpenClaw as non-root user), we'll cover it in a future article when we deep dive into securing it.
- Consider sandboxing with Docker inside the VM for extra isolation.
Testing and Validation
After setup, run a smoke test:
- Access the UI via Tailscale URL.
- Send a test message via WhatsApp/Telegram: "Hello, what's the weather in Dubai?" (assuming LLM integration).
- Verify browser control: Ask it to open a safe site and scrape non-sensitive data.
- Check logs for errors:
openclaw gateway logs.
If issues arise, use the troubleshooting section.
The Template Script (Full Code)
Here is the full code for reference (you can copy-paste this into a file if the wget fails, but using the raw URL is recommended):
#!/bin/bash
# ===== PRODUCTION-READY OPENCLAW PROXMOX TEMPLATE =====
# This script creates a Debian 13 VM template with OpenClaw pre-installed
#
# Author: Ali Shaikh <alishaikh.me>
#
# Features:
# - Cloud-init progress indication
# - Login warnings during initialization
# - Automatic pnpm setup
# - Status check commands
# - Access info helper command
# - Proper error handling
#
# ===== CONFIGURABLE PARAMETERS =====
VMID=10010
VM_NAME="debian-13-openclaw-ready-template"
STORAGE="local-lvm"
MEMORY=4096
SOCKETS=1
CORES=2
DISK_ADDITIONAL_SIZE="+15G"
IMAGE_URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
IMAGE_PATH="/var/lib/vz/template/iso/$(basename ${IMAGE_URL})"
CI_USER="debian"
CI_PASSWORD="$(openssl passwd -6 debian)" # For production, use a stronger password
# Replace with your actual SSH public key
echo "ssh-rsa " > ~/ssh.pub
# Download Debian 13 Cloud Image
if [ ! -f "${IMAGE_PATH}" ]; then
echo "Downloading Debian 13 Cloud Image..."
wget -P /var/lib/vz/template/iso/ "${IMAGE_URL}"
else
echo "Image already exists at ${IMAGE_PATH}"
fi
set -x
# Destroy existing VM if it exists
qm destroy $VMID 2>/dev/null || true
# Create VM
qm create ${VMID} \
--name "${VM_NAME}" \
--ostype l26 \
--memory ${MEMORY} \
--cores ${CORES} \
--agent 1 \
--bios ovmf --machine q35 --efidisk0 ${STORAGE}:0,pre-enrolled-keys=0 \
--cpu host --socket ${SOCKETS} --cores ${CORES} \
--vga serial0 --serial0 socket \
--net0 virtio,bridge=vmbr0
# Import disk
qm importdisk ${VMID} "${IMAGE_PATH}" ${STORAGE}
qm set $VMID --scsihw virtio-scsi-pci --virtio0 $STORAGE:vm-${VMID}-disk-1,discard=on
qm resize ${VMID} virtio0 ${DISK_ADDITIONAL_SIZE}
qm set $VMID --boot order=virtio0
qm set $VMID --scsi1 $STORAGE:cloudinit
# Create snippets directory
mkdir -p /var/lib/vz/snippets/
# Create Cloud-Init configuration with proper YAML formatting
cat << 'CLOUDCONFIG' | tee /var/lib/vz/snippets/debian13_openclaw_ready.yaml
#cloud-config
package_update: true
package_upgrade: true
packages:
- curl
- wget
- git
- build-essential
- procps
- file
- ca-certificates
- gnupg
- qemu-guest-agent
- htop
- vim
- tmux
- jq
- cron
write_files:
# Login status checker - runs on every login to show progress
- path: /etc/profile.d/cloud-init-status.sh
permissions: '0755'
owner: root:root
content: |
#!/bin/bash
# Check cloud-init and installation status on login
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Check if cloud-init is still running
if cloud-init status 2>/dev/null | grep -q "running"; then
echo ""
echo -e "${YELLOW}========================================${NC}"
echo -e "${YELLOW} SYSTEM INITIALISATION IN PROGRESS${NC}"
echo -e "${YELLOW}========================================${NC}"
echo ""
echo -e "${BLUE}Cloud-init is still running. Please wait...${NC}"
echo ""
echo "Monitor progress:"
echo " sudo tail -f /var/log/cloud-init-output.log"
echo " sudo tail -f /var/log/openclaw-install.log"
echo ""
echo "Check status:"
echo " cloud-init status"
echo " cat /var/run/openclaw-install-progress"
echo ""
echo -e "${YELLOW}Some commands may not be available yet!${NC}"
echo ""
elif [ -f /var/run/openclaw-install-progress ]; then
PROGRESS=$(cat /var/run/openclaw-install-progress 2>/dev/null)
if [ "$PROGRESS" != "COMPLETE" ]; then
echo ""
echo -e "${YELLOW}Installation in progress: $PROGRESS${NC}"
echo "Run: sudo tail -f /var/log/openclaw-install.log"
echo ""
fi
elif [ -f /root/.openclaw-ready ]; then
# Only show ready message once per session
if [ -z "$OPENCLAW_READY_SHOWN" ]; then
echo ""
echo -e "${GREEN}System is ready! Run: setup-openclaw.sh${NC}"
echo ""
export OPENCLAW_READY_SHOWN=1
fi
fi
- path: /usr/local/bin/openclaw-access-info
permissions: '0755'
owner: root:root
content: |
#!/bin/bash
echo "==========================================="
echo "OpenClaw Access Information"
echo "==========================================="
echo ""
echo "Tailscale Serve URL:"
tailscale serve status 2>/dev/null | grep -o 'https://[^ ]*' || echo " Not configured - run: openclaw onboard --install-daemon"
echo ""
if [ -f ~/.openclaw/openclaw.json ]; then
echo "Gateway Token:"
cat ~/.openclaw/openclaw.json | jq -r '.gateway.auth.token' 2>/dev/null || echo " Not found"
echo ""
echo "Allow Tailscale Auth:"
cat ~/.openclaw/openclaw.json | jq -r '.gateway.auth.allowTailscale' 2>/dev/null || echo " Not configured"
else
echo "OpenClaw not configured yet"
echo "Run: openclaw onboard --install-daemon"
fi
echo "==========================================="
- path: /usr/local/bin/setup-openclaw.sh
permissions: '0755'
owner: root:root
content: |
#!/bin/bash
echo "======================================"
echo "OpenClaw Setup Helper"
echo "======================================"
echo ""
echo "Installed versions:"
echo " Node.js: $(node --version 2>/dev/null || echo 'Not found')"
echo " npm: $(npm --version 2>/dev/null || echo 'Not found')"
echo " pnpm: $(pnpm --version 2>/dev/null || echo 'Not installed')"
echo " OpenClaw: $(openclaw --version 2>/dev/null || echo 'Not installed - run: npm install -g openclaw@latest')"
echo " Tailscale: $(tailscale version 2>/dev/null || echo 'Not found')"
echo " Homebrew: $(brew --version 2>/dev/null | head -n1 || echo 'Not found - source ~/.bashrc first')"
echo ""
echo "Quick Start Guide:"
echo "=================="
echo ""
echo "1. Connect to Tailscale:"
echo " Get auth key: https://login.tailscale.com/admin/settings/keys"
echo " sudo tailscale up --authkey=tskey-auth-YOUR_KEY"
echo ""
echo "2. Configure pnpm (required for skills):"
echo " pnpm setup && source ~/.bashrc"
echo ""
echo "3. Configure OpenClaw:"
echo " openclaw onboard --install-daemon"
echo ""
echo " Configuration choices:"
echo " - Setup: Local gateway (this machine)"
echo " - Bind: Loopback (127.0.0.1) <- REQUIRED"
echo " - Auth: Token (Recommended)"
echo " - Tailscale: Serve (for HTTPS)"
echo ""
echo "4. Get access info:"
echo " openclaw-access-info"
echo ""
echo "5. Verify:"
echo " openclaw status"
echo " openclaw health"
echo " tailscale serve status"
echo ""
echo "Docs: https://docs.openclaw.ai"
echo ""
# Progress checker command
- path: /usr/local/bin/check-install-status
permissions: '0755'
owner: root:root
content: |
#!/bin/bash
echo "========================================"
echo "Installation Status Check"
echo "========================================"
echo ""
# Cloud-init status
echo "Cloud-init status:"
cloud-init status 2>/dev/null || echo " Unable to check"
echo ""
# Progress file (cleared after reboot, so may not exist)
if [ -f /var/run/openclaw-install-progress ]; then
echo "Current step: $(cat /var/run/openclaw-install-progress)"
fi
# Ready marker (use sudo to check /root)
if sudo test -f /root/.openclaw-ready 2>/dev/null; then
echo "Status: READY"
else
echo "Status: IN PROGRESS (or checking permissions...)"
fi
echo ""
# Installed versions (use sudo to read /root)
if sudo test -f /root/install-versions.txt 2>/dev/null; then
echo "Installed versions:"
sudo cat /root/install-versions.txt | sed 's/^/ /'
fi
echo ""
echo "Logs:"
echo " sudo tail -f /var/log/openclaw-install.log"
- path: /root/install-openclaw.sh
permissions: '0755'
owner: root:root
content: |
#!/bin/bash
# Don't use set -e - we want to continue even if some commands fail
exec > /var/log/openclaw-install.log 2>&1
# Progress update function
update_progress() {
echo "$1" > /var/run/openclaw-install-progress
echo ">>> PROGRESS: $1"
}
echo "=== OpenClaw Installation Started at $(date) ==="
update_progress "Starting installation..."
# Install Tailscale
update_progress "Installing Tailscale..."
curl -fsSL https://tailscale.com/install.sh | sh
if command -v tailscale &> /dev/null; then
echo "Tailscale version: $(tailscale version)"
else
echo "WARNING: Tailscale installation may have failed"
fi
# Install Node.js 24
update_progress "Installing Node.js 24..."
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
apt-get install -y nodejs
echo "Node.js version: $(node --version)"
echo "npm version: $(npm --version)"
# Install pnpm globally
update_progress "Installing pnpm..."
npm install -g pnpm@latest
echo "pnpm version: $(pnpm --version)"
# Setup pnpm global bin directory (required for OpenClaw skills)
pnpm setup
source /root/.bashrc 2>/dev/null || true
# Also setup for debian user
sudo -u debian bash -c 'pnpm setup' 2>/dev/null || true
# Install OpenClaw via npm
update_progress "Installing OpenClaw..."
npm install -g openclaw@latest
# Verify installation and add to PATH if needed
if command -v openclaw &> /dev/null; then
echo "OpenClaw version: $(openclaw --version)"
else
echo "OpenClaw not in PATH, checking npm global bin..."
NPM_BIN=$(npm bin -g)
if [ -f "$NPM_BIN/openclaw" ]; then
echo "Found at $NPM_BIN/openclaw, creating symlink..."
ln -sf "$NPM_BIN/openclaw" /usr/local/bin/openclaw
echo "OpenClaw version: $(openclaw --version)"
else
echo "ERROR: OpenClaw installation failed"
echo "NPM global bin: $NPM_BIN"
ls -la "$NPM_BIN/" 2>/dev/null || echo "Cannot list npm bin directory"
fi
fi
# Install Homebrew for debian user (for plugins)
update_progress "Installing Homebrew (this may take a while)..."
# Create the debian user's home if it doesn't exist
mkdir -p /home/debian
chown debian:debian /home/debian
# Install Homebrew as debian user
sudo -u debian bash -c 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' || {
echo "WARNING: Homebrew installation failed"
}
# Add Homebrew to debian user's PATH
if [ -d "/home/linuxbrew/.linuxbrew" ]; then
sudo -u debian bash -c 'echo "# Homebrew" >> ~/.bashrc'
sudo -u debian bash -c 'echo "eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"" >> ~/.bashrc'
echo "Homebrew installed successfully"
else
echo "WARNING: Homebrew directory not found after installation"
fi
update_progress "Finalising..."
echo "=== Installation Complete at $(date) ==="
echo "Node.js: $(node --version 2>/dev/null || echo 'FAILED')" > /root/install-versions.txt
echo "npm: $(npm --version 2>/dev/null || echo 'FAILED')" >> /root/install-versions.txt
echo "pnpm: $(pnpm --version 2>/dev/null || echo 'FAILED')" >> /root/install-versions.txt
echo "OpenClaw: $(openclaw --version 2>/dev/null || echo 'FAILED')" >> /root/install-versions.txt
echo "Tailscale: $(tailscale version 2>/dev/null || echo 'FAILED')" >> /root/install-versions.txt
echo "Homebrew: $(sudo -u debian /home/linuxbrew/.linuxbrew/bin/brew --version 2>/dev/null | head -n1 || echo 'FAILED')" >> /root/install-versions.txt
# Mark installation as complete
update_progress "COMPLETE"
touch /root/.openclaw-ready
echo ""
echo "Check /root/install-versions.txt for results"
runcmd:
- systemctl enable qemu-guest-agent
- systemctl start qemu-guest-agent
- echo "INITIALISING" > /var/run/openclaw-install-progress
- chmod +x /root/install-openclaw.sh
- /root/install-openclaw.sh
- chown debian:debian /usr/local/bin/setup-openclaw.sh
- |
cat >> /etc/motd << 'MOTD'
========================================
OpenClaw Ready Template
========================================
Created by Ali Shaikh
https://alishaikh.me
Pre-installed (globally):
- Node.js 24 (NodeSource)
- pnpm (latest)
- OpenClaw CLI
- Tailscale (official)
- Homebrew (for plugins)
Check installation: cat /root/install-versions.txt
Check status: check-install-status
Setup guide: setup-openclaw.sh
Access info: openclaw-access-info (after configuration)
MOTD
- apt-get clean
- apt-get autoremove -y
power_state:
mode: reboot
message: "Rebooting after OpenClaw installation"
timeout: 30
condition: True
CLOUDCONFIG
# Apply Cloud-Init configuration
qm set $VMID --cicustom "vendor=local:snippets/debian13_openclaw_ready.yaml"
qm set $VMID --tags debian-template,openclaw-ready,nodejs24,production
qm set ${VMID} --ciuser ${CI_USER} --cipassword "${CI_PASSWORD}"
qm set $VMID --sshkeys ~/ssh.pub
qm set $VMID --ipconfig0 ip=dhcp
# Convert to template
qm template ${VMID}
echo ""
echo "=========================================="
echo "PRODUCTION TEMPLATE CREATED SUCCESSFULLY"
echo "=========================================="
echo "Template ID: ${VMID}"
echo "Template Name: ${VM_NAME}"
echo ""
echo "Globally installed:"
echo " - Node.js 24 (NodeSource)"
echo " - pnpm (latest)"
echo " - OpenClaw CLI"
echo " - Tailscale (official)"
echo " - Homebrew (for plugins)"
echo ""
echo "DEPLOYMENT:"
echo " qm clone ${VMID} 201 --name openclaw-prod --full"
echo " qm start 201"
echo ""
echo "IMPORTANT: Wait 5-7 minutes for installation to complete"
echo ""
echo "CHECK INSTALLATION STATUS:"
echo " ssh debian@<vm-ip>"
echo " check-install-status # Quick status check"
echo " tail -f /var/log/openclaw-install.log"
echo " cat /root/install-versions.txt"
echo ""
Conclusion
You now have a production-ready AI assistant running on your own infrastructure, accessible from anywhere via WhatsApp, Telegram, or your browser, with zero public exposure. Your conversations and command history stay on your server, your files remain under your control, and you decide exactly what the AI can access. However, be clear about the privacy boundary: when you use cloud models like Claude, GPT-4o, or Gemini, your prompts and responses still go to Anthropic, OpenAI, or Google. Self-hosting OpenClaw gives you infrastructure control and data residency for everything except the LLM API calls. For true end-to-end privacy, you'd need to run local models via Ollama or LM Studio, though at the cost of performance compared to cloud models.
Before you start feeding your assistant sensitive data, remember what you've built: a system that can execute arbitrary commands, access files, and interact with external services. The security measures in this guide (loopback binding, token authentication, Tailscale isolation) are foundational, not exhaustive. Treat this like any other privileged service: rotate credentials regularly, monitor access logs, keep the system patched, and never expose the gateway publicly. The Proxmox snapshot feature is your friend - take one before major changes so you can roll back easily.
The Proxmox template makes this repeatable: clone it whenever you need a fresh instance, whether for testing new configurations or deploying assistants for different use cases. OpenClaw is still evolving rapidly (remember, it went from zero to 100k stars in two months), so expect frequent updates and new capabilities. Consider this deployment a learning platform first, a production tool second. Test destructively in clones, understand the permission model, and gradually increase what you trust it to do. Report issues or contribute on the GitHub repo.
Self-hosting means self-responsibility, but it also means you're not at the mercy of a vendor's API changes or pricing decisions. Join the community, experiment with custom skills, contribute back what you learn, and most importantly, keep your gateway token secure. Stay vigilant, stay curious, and enjoy having an AI that actually works for you. Happy automating! 🦞
Member discussion