A tested, step-by-step guide for running OpenClaw on a Google Cloud VM for under $20/month. Covers Telegram integration, security hardening, and real-world pitfalls discovered during deployment.
Tested with: OpenClaw 2026.2.6 on GCP e2-small, Ubuntu 24.04 LTS, Node.js 22.22.0
Last updated: February 2026
OpenClaw is an open-source AI agent gateway. It connects language models (Claude, GPT, Gemini, etc.) to messaging platforms — Telegram, WhatsApp, Discord, iMessage, Slack, Signal, and more. You self-host it, it runs 24/7, and your AI agent can send/receive messages, execute tasks, run on schedules, and maintain memory across conversations.
Key capabilities:
- Multi-platform messaging with a single agent
- Group chats with mention-gating
- Cron jobs, heartbeat checks, webhooks
- Skills system (extensible behavior modules)
- Long-term memory (daily logs + curated facts via vector search)
- Media pipeline (images, audio, documents)
- WebChat, macOS/iOS/Android clients
- MIT license
Official resources:
- Docs: https://docs.openclaw.ai/
- GitHub: https://github.com/openclaw/openclaw
- Security: https://docs.openclaw.ai/gateway/security
Read this section before installation.
- Malicious Skills on ClawHub — 341 skills found stealing credentials via Atomic Stealer malware. Do not install third-party skills without reviewing source code.
- RCE + Command Injection CVEs — Multiple vulnerabilities patched in recent releases. Always run the latest version.
- Node.js 22.12.0+ required — Earlier versions have unpatched vulnerabilities.
These are trade-offs of running an AI agent with tool access:
- Credentials stored as files on the filesystem (config, OAuth tokens, channel tokens)
- AI agent can execute shell commands within its permission scope
- Prompt injection is unsolved — incoming messages can attempt to manipulate the model
- Session transcripts contain conversation history — readable by anyone with filesystem access
- Gateway on loopback — never expose port 18789 to the internet
- No public IP on the VM — use Tailscale (or similar VPN) for remote access
- Pairing mode for DMs — require explicit approval for new contacts
- Run
openclaw security audit --deepafter every configuration change - File permissions — 700 for directories, 600 for config/credential files
- Modern models only for tool-enabled agents — they resist prompt injection better
- Never install unverified skills from any marketplace
- GCP account with active billing (free trial credits work)
- Tailscale account (free tier is sufficient)
- Telegram account (for creating a bot via @BotFather)
gcloudCLI installed on your local machine- AI provider API key (Anthropic, OpenAI, Google, etc.) or a subscription with OAuth support
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 2 GB (+ swap) | 4 GB |
| Disk | 20 GB SSD | 30 GB SSD |
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| Node.js | 22.12.0+ | Latest 22.x |
Observed resource usage (e2-small, idle gateway + Telegram channel):
- RAM: ~830 MB of 1.9 GB (+ 2 GB swap barely touched)
- Disk: ~6.7 GB of 30 GB
- CPU: <1% idle
- GCP Project Setup
- Create the VM
- Tailscale and Network
- Install OpenClaw
- Configuration
- Telegram Channel
- Systemd Service
- Security Hardening
- Cron Jobs and Automation
- Management and Troubleshooting
# Create a project (or use an existing one)
gcloud projects create <PROJECT_ID> --name="<PROJECT_NAME>"
# Link billing
gcloud billing projects link <PROJECT_ID> --billing-account=<BILLING_ACCOUNT_ID>
# Enable Compute Engine API
gcloud services enable compute.googleapis.com --project=<PROJECT_ID>
# Set a budget alert (recommended — prevents surprise bills)
gcloud services enable billingbudgets.googleapis.com --project=<PROJECT_ID>
gcloud billing budgets create \
--billing-account=<BILLING_ACCOUNT_ID> \
--display-name="openclaw-budget" \
--budget-amount=50USD \
--filter-projects="projects/<PROJECT_ID>" \
--threshold-rule=percent=50 \
--threshold-rule=percent=90 \
--threshold-rule=percent=100| Type | vCPU | RAM | $/month | Verdict |
|---|---|---|---|---|
| e2-micro | 2 (shared) | 1 GB | ~$7 | Too little RAM. Gateway OOMs. |
| e2-small | 2 (shared) | 2 GB | ~$17 | Works with swap. Tested. |
| e2-medium | 2 (shared) | 4 GB | ~$28 | Comfortable. No swap needed. |
| e2-standard-2 | 2 | 8 GB | ~$54 | Overkill for OpenClaw alone. |
Prices include 30 GB SSD. us-central1 region. Prices may vary.
gcloud compute instances create openclaw \
--project=<PROJECT_ID> \
--zone=us-central1-a \
--machine-type=e2-small \
--image-family=ubuntu-2404-lts-amd64 \
--image-project=ubuntu-os-cloud \
--boot-disk-size=30GB \
--boot-disk-type=pd-ssd \
--tags=allow-ssh \
--metadata=enable-oslogin=TRUEWith 2 GB RAM the gateway uses ~830 MB at idle, but spikes during model calls. Swap prevents OOM kills.
# SSH in first
gcloud compute ssh openclaw --zone=us-central1-a --project=<PROJECT_ID>
# Create 2 GB swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# Verify: should show 2 GB swap
free -hgcloud compute firewall-rules create allow-ssh \
--project=<PROJECT_ID> \
--direction=INGRESS \
--priority=1000 \
--network=default \
--action=ALLOW \
--rules=tcp:22 \
--source-ranges=0.0.0.0/0 \
--target-tags=allow-ssh| Component | Cost |
|---|---|
| e2-small VM | ~$12 |
| 30 GB SSD | ~$5 |
| Cloud NAT | ~$1 |
| Total | ~$18/mo |
Tailscale creates an encrypted mesh VPN between your devices. Once configured, you can SSH to the VM by hostname (e.g., ssh root@openclaw) without a public IP. Free for personal use.
# On the VM (via gcloud SSH while public IP still exists)
curl -fsSL https://tailscale.com/install.sh | sh
# Start Tailscale — this prints an auth URL
sudo tailscale login --ssh
# Open the URL in your browser to authorize the machineGotcha:
tailscale up --sshblocks waiting for browser auth and can hang in non-interactive SSH sessions. Usetailscale login --sshinstead — it prints the URL and exits.
Download from https://tailscale.com/download. Log in with the same account.
After removing the public IP, the VM needs Cloud NAT for outgoing traffic (API calls, npm installs, Telegram polling).
# Create a Cloud Router
gcloud compute routers create nat-router \
--project=<PROJECT_ID> \
--network=default \
--region=us-central1
# Create Cloud NAT
gcloud compute routers nats create nat-config \
--project=<PROJECT_ID> \
--router=nat-router \
--region=us-central1 \
--nat-all-subnet-ip-ranges \
--auto-allocate-nat-external-ipsgcloud compute instances delete-access-config openclaw \
--zone=us-central1-a \
--project=<PROJECT_ID> \
--access-config-name="external-nat"# From your local machine
tailscale status # Should list "openclaw"
ssh root@openclaw # Should connect via Tailscale
# On the VM — verify outbound works via NAT
curl -sI https://google.com | head -3Gotcha: First Tailscale SSH connection may require browser re-auth. The VM will print an auth URL — open it to approve.
Run as root on the VM:
useradd -m -s /bin/bash openclawcurl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
node --version # Must be v22.12.0 or laterSwitch to the openclaw user:
su - openclaw
curl -fsSL https://openclaw.ai/install.sh | bashThe installer will print a PATH warning. Fix it:
# The installer puts the binary in ~/.npm-global/bin/, not ~/.openclaw/bin/
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
openclaw --versionGotcha: The installer's PATH suggestion in the output is wrong — it concatenates the existing
$PATHinto the export line. Use the simplified version above.
If you have a direct TTY (not piped SSH), run:
openclaw onboard --install-daemonThis wizard walks you through provider auth, gateway setup, and channel config.
Gotcha: Onboarding requires an interactive terminal. It fails via
ssh host "command"or in scripts. If you're setting up headless, skip onboarding and configure manually (next section).
If onboarding ran successfully, skip this section. Otherwise, create the config manually.
su - openclaw
mkdir -p ~/.openclaw/agents/main/agent
mkdir -p ~/.openclaw/agents/main/sessions
mkdir -p ~/.openclaw/credentials
mkdir -p ~/.openclaw/workspace/memory
mkdir -p ~/.openclaw/workspace/skillsGotcha: The
agents/main/sessionsdirectory is not auto-created. Without it,openclaw doctorreports a critical error.
cat > ~/.openclaw/openclaw.json << 'EOF'
{
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-opus-4-6"
},
"workspace": "/home/openclaw/.openclaw/workspace",
"compaction": {
"mode": "safeguard"
}
}
},
"channels": {
"telegram": {
"enabled": true,
"botToken": "<YOUR_TELEGRAM_BOT_TOKEN>",
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"streamMode": "partial",
"groups": { "*": { "requireMention": true } }
}
},
"gateway": {
"port": 18789,
"mode": "local",
"bind": "loopback",
"auth": {
"mode": "token",
"token": "<GENERATE_WITH_openssl_rand_-hex_24>"
}
},
"discovery": {
"mdns": { "mode": "off" }
},
"logging": {
"redactSensitive": "tools"
}
}
EOF
chmod 600 ~/.openclaw/openclaw.jsonReplace:
<YOUR_TELEGRAM_BOT_TOKEN>— from @BotFather<GENERATE_WITH_openssl_rand_-hex_24>— runopenssl rand -hex 24to generate
Model options:
- Anthropic:
anthropic/claude-opus-4-6,anthropic/claude-sonnet-4 - OpenAI:
openai/gpt-5.2,openai-codex/gpt-5.2(subscription OAuth) - Google:
google/gemini-2.5-pro
For subscription-based OAuth (OpenAI Codex, Claude subscription), you need interactive onboarding or manual auth-profiles.json setup. API key auth is simpler for headless setups.
For API key providers, set keys via environment variables:
cat > ~/.openclaw/gateway.env << 'EOF'
ANTHROPIC_API_KEY=sk-ant-...
PATH=/home/openclaw/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
HOME=/home/openclaw
EOF
chmod 600 ~/.openclaw/gateway.env- Open @BotFather in Telegram
- Send
/newbot, follow the prompts - Copy the bot token
Optional BotFather settings:
/setjoingroups— control whether the bot can be added to groups/setprivacy— message visibility in groups (disable for@mentionmode)
With dmPolicy: "pairing" (default):
- Send any message to your bot in Telegram
- The bot replies with a pairing code (valid 1 hour)
- Approve on the server:
openclaw pairing approve telegram <CODE>After approval, the bot responds to your messages.
Alternative — allowlist (skip pairing):
openclaw config set channels.telegram.dmPolicy allowlist
openclaw config set channels.telegram.allowFrom '["YOUR_TELEGRAM_USER_ID"]'Find your user ID: send a message to the bot and check openclaw logs --follow — it logs the sender ID.
If openclaw onboard --install-daemon didn't run (headless setup), create the service manually:
# /etc/systemd/system/openclaw.service
[Unit]
Description=OpenClaw Gateway
After=network.target
[Service]
Type=simple
User=openclaw
Group=openclaw
WorkingDirectory=/home/openclaw
EnvironmentFile=/home/openclaw/.openclaw/gateway.env
ExecStart=/home/openclaw/.npm-global/bin/openclaw gateway run
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetGotcha: The binary path is
~/.npm-global/bin/openclaw, not~/.openclaw/bin/openclaw. The installer uses npm's global prefix, not the~/.openclawdirectory.
sudo systemctl daemon-reload
sudo systemctl enable openclaw
sudo systemctl start openclaw
sudo systemctl status openclawVerify the gateway is listening:
ss -tlnp | grep 18789
# Should show: 127.0.0.1:18789 LISTENopenclaw security audit --deep
openclaw security audit --fix # Auto-fixes common issuesGotcha: The installer creates
~/.openclaw/credentials/with 775 permissions. The audit flags this as CRITICAL. Run--fixor manuallychmod 700all directories.
chmod 700 ~/.openclaw
chmod 700 ~/.openclaw/credentials
chmod 700 ~/.openclaw/agents
chmod 700 ~/.openclaw/agents/main
chmod 700 ~/.openclaw/agents/main/agent
chmod 600 ~/.openclaw/openclaw.json
chmod 600 ~/.openclaw/gateway.env
chmod 600 ~/.openclaw/credentials/*
chmod 600 ~/.openclaw/agents/*/agent/auth-profiles.json{
gateway: {
mode: "local",
bind: "loopback", // Never change to 0.0.0.0
auth: { mode: "token" } // Always require auth
},
channels: {
telegram: {
dmPolicy: "pairing", // Or "allowlist"
groupPolicy: "allowlist",
groups: { "*": { requireMention: true } }
}
},
discovery: {
mdns: { mode: "off" } // Prevents info disclosure on LAN
},
logging: {
redactSensitive: "tools" // Redact tool args in logs
}
}Isolate tool execution in containers:
sudo apt-get install -y docker.io
sudo usermod -aG docker openclaw
# Log out and back in for group to take effect{
agents: {
defaults: {
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "rw" // "ro" or "none" for stricter isolation
}
}
}
}Restrict dangerous tools for non-owner agents:
{
agents: {
defaults: {
tools: {
deny: ["browser", "web_fetch", "exec"]
}
}
}
}- Never install skills from untrusted sources (Feb 2026: 341 malicious ClawHub skills found)
- Review
SKILL.mdcontents before installing any skill - Skills can read environment variables and execute code — treat as trusted code
- Per-agent skills:
~/.openclaw/workspace/skills/ - Shared skills:
~/.openclaw/skills/
- Store API keys in
gateway.env(permissions 600), not inline in config - Use
EnvironmentFile=in systemd, notEnvironment= - Rotate all tokens after any suspected compromise
- Never commit credentials to git
If you suspect compromise:
- Stop:
systemctl stop openclaw - Isolate: Verify
gateway.bindisloopback - Rotate: Gateway token, model API keys, channel bot tokens
- Review: Logs at
/tmp/openclaw/openclaw-YYYY-MM-DD.logand session transcripts at~/.openclaw/agents/<agentId>/sessions/*.jsonl - Audit:
openclaw security audit --deep - Restart only after audit is clean
# Daily morning briefing at 10:00 (delivered to Telegram)
openclaw cron add \
--name "Morning Briefing" \
--cron "0 10 * * *" \
--tz "America/New_York" \
--session isolated \
--message "Good morning. Check MEMORY.md and summarize today's priorities." \
--deliver \
--channel telegram \
--to "<YOUR_TELEGRAM_USER_ID>"
# Weekly review every Monday at 11:00
openclaw cron add \
--name "Weekly Review" \
--cron "0 11 * * 1" \
--tz "America/New_York" \
--session isolated \
--message "Weekly review. Read memory logs from the past 7 days and summarize progress." \
--deliver \
--channel telegram \
--to "<YOUR_TELEGRAM_USER_ID>"openclaw cron list # List all jobs with next run time
openclaw cron run <JOB_ID> # Trigger manually
openclaw cron remove <JOB_ID> # Delete a jobOpenClaw runs a heartbeat check every 30 minutes by default. Configure checks in ~/.openclaw/workspace/HEARTBEAT.md. If nothing requires attention, the agent responds with HEARTBEAT_OK (no notification sent).
# Status and health
openclaw status # Overview of gateway, channels, sessions
openclaw status --deep # With live probes
openclaw health # System health check
openclaw doctor # Validate config, detect issues, migrate legacy
openclaw doctor --fix # Auto-fix detected issues
# Gateway control
openclaw gateway status
openclaw gateway restart
openclaw gateway stop
openclaw gateway --force # Kill and restart
# Channels
openclaw channels status # Connection status for all channels
openclaw logs --follow # Live gateway logs
# Security
openclaw security audit
openclaw security audit --deep
openclaw security audit --fix
# Memory
openclaw memory search "query"
# Update
curl -fsSL https://openclaw.ai/install.sh | bash # Re-run installer
openclaw doctor && openclaw gateway restart # Post-updateThe Control UI runs at http://127.0.0.1:18789/. Access it via:
SSH tunnel:
ssh -N -L 18789:127.0.0.1:18789 user@gateway-host
# Open http://localhost:18789/ in your browserTailscale Serve:
{
gateway: {
tailscale: { mode: "serve" }
}
}Access at https://<hostname>.<tailnet>.ts.net/
Gateway won't start:
journalctl -u openclaw --no-pager -n 50 # Check systemd logs
openclaw gateway --force --verbose # Force restart with debug outputTelegram not responding:
openclaw channels status # Check channel state
openclaw logs --follow # Watch for errorsCheck that the bot token is correct and that no other instance is polling the same token (Telegram allows only one poller per bot token).
Tailscale SSH needs re-auth:
sudo tailscale login --ssh # Prints a new auth URLNo internet after removing public IP:
Verify Cloud NAT is configured:
curl -sI https://google.com | head -1 # Should return HTTP/2 301If it fails, check that the Cloud Router and NAT exist in the same region as the VM.
Bootstrap files not taking effect:
Send /new in the chat to start a fresh session. Bootstrap files (SOUL.md, USER.md, MEMORY.md, etc.) are only read at session start, not mid-conversation.
Legacy migration (from Moltbot/Clawdbot):
openclaw doctor --fix # Auto-migrates legacy paths and config keys| Path | Purpose | Permissions |
|---|---|---|
~/.openclaw/ |
Config root | 700 |
~/.openclaw/openclaw.json |
Main config | 600 |
~/.openclaw/gateway.env |
Environment variables | 600 |
~/.openclaw/credentials/ |
Channel tokens, OAuth | 700 |
~/.openclaw/agents/<id>/ |
Agent data | 700 |
~/.openclaw/agents/<id>/sessions/ |
Conversation transcripts | 700 |
~/.openclaw/workspace/ |
Agent workspace (bootstrap files, skills, memory) | 700 |
~/.npm-global/bin/openclaw |
CLI binary (npm global install) | — |
/tmp/openclaw/ |
Logs | — |
Place these in ~/.openclaw/workspace/ to customize the agent:
| File | Purpose |
|---|---|
SOUL.md |
Persona, tone, behavioral boundaries |
USER.md |
Information about you (the operator) |
IDENTITY.md |
Agent name, emoji, vibe |
MEMORY.md |
Long-term curated memory |
AGENTS.md |
Operational instructions |
TOOLS.md |
Tool usage guidelines |
HEARTBEAT.md |
Periodic check tasks |
Empty files are skipped. Send /new in chat after editing to reload.
Issues discovered during real deployment:
| Issue | Cause | Fix |
|---|---|---|
openclaw: command not found |
Installer uses ~/.npm-global/bin/, not ~/.openclaw/bin/ |
Add ~/.npm-global/bin to PATH |
| Onboarding fails via SSH | Requires interactive TTY (/dev/tty) |
Configure manually or use a direct terminal |
| OAuth auth fails headless | OAuth flow requires browser redirect | Use API key auth for headless, or copy auth-profiles.json from another machine |
credentials dir writable by others |
Installer creates dirs with 775 | chmod 700 ~/.openclaw/credentials |
Session store dir missing |
Not auto-created on fresh install | mkdir -p ~/.openclaw/agents/main/sessions |
| Two bots fight over token | Telegram allows one poller per token | Stop old instance before starting new one |
| VM OOM on e2-small | 2 GB RAM, gateway spikes during inference | Add 2 GB swap file |
- OpenClaw Documentation
- Security Guide
- Getting Started
- Updating
- Cron Jobs
- Skills
- Telegram Channel
- Tailscale Setup
This guide is provided as-is under the MIT License. Not affiliated with OpenClaw.