How to self-host OpenClaw on a VPS
OpenClaw is an MIT-licensed AI assistant platform that connects to 23+ messaging channels — WhatsApp, Telegram, Slack, Discord, and more. Running it on your own VPS means your data stays on your hardware, you pick the LLM provider, and you pay only for compute. This guide covers installation, BYOK configuration, channel setup, and production HTTPS with Caddy.
Requirements
- Ubuntu 22.04+ / Debian 12 / macOS / Windows WSL2
- Node.js 22.19+ or Node.js 24 (LTS recommended)
- A VPS with at least 1 GB RAM and a public IP address
- An API key for Claude 4, GPT-4o, Gemini, or DeepSeek (BYOK)
- A domain or subdomain pointing at your VPS (for HTTPS)
Step 1 — Install Node.js 22
# Install Node.js 22 LTS via NodeSource
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node --version # should print v22.x.x or higherStep 2 — Install OpenClaw
The quickest path is the global npm package with the built-in daemon installer:
npm install -g openclaw@latest
openclaw onboard --install-daemonThe --install-daemon flag writes a systemd unit so OpenClaw starts on boot automatically. You can still run it manually with openclaw start.
Step 3 — Add your BYOK API key
OpenClaw supports multiple providers. Set the key for the model you want to use:
openclaw config set llm.provider anthropic
openclaw config set llm.apiKey sk-ant-...
openclaw config set llm.model claude-opus-5Step 4 — Connect messaging channels
OpenClaw supports 23+ channels. Here are the two most common:
Telegram
# 1. Create a bot with @BotFather in Telegram — copy the token
# 2. Register it with OpenClaw:
openclaw channels add telegram --token 123456789:ABCdef...WhatsApp (via Meta Business API)
# Follow Meta's Business API setup to get your token and phone number ID
openclaw channels add whatsapp \
--token EAAG... \
--phone-number-id 123456789012345Run openclaw channels list to see all supported integrations including Slack, Discord, Signal, and SMS.
Step 5 — HTTPS with Caddy
Caddy automatically provisions and renews TLS certificates via Let's Encrypt. Install it and create a simple Caddyfile:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddyReplace agent.yourdomain.com with your actual subdomain and ensure port 80/443 are open in your firewall. OpenClaw defaults to port 3000.
Step 6 — Verify and maintain
# Check status
openclaw status
# View logs
journalctl -u openclaw -f
# Update to the latest version
npm install -g openclaw@latest
sudo systemctl restart openclaw“Running” is not the same as “working”
openclaw status tells you the process is alive. It does not tell you the agent can reach your LLM provider — those are separate failures, and the second one is invisible until someone actually asks it something. We provision this stack repeatedly for testing, and we have had installs finish completely clean, the service report healthy, and the agent still be unable to answer a single prompt because the provider rejected the key.
So finish by sending one real message through a channel you connected in Step 4, with journalctl -u openclaw -f open in another terminal. A reply is the only proof that the install, the key, the model name and the provider account are all correct at once.
Troubleshooting — what actually goes wrong
We install this stack on fresh, disposable VPS instances continuously to test our own provisioning. Almost none of the failures we see are the install itself — they cluster at the boundary between the agent and the model provider, which is the part no install script can verify for you.
The agent installs fine and answers nothing
By far the most common outcome. The install script exits 0, the service is running, and every prompt fails, because the model provider is rejecting the request. Provider errors surface in the agent's log rather than at install time, and they are usually about the account rather than the code — an exhausted credit balance, a spend cap, a key scoped to the wrong model. A real example, taken verbatim from one of our runs where an OpenRouter key had hit its cap:
HTTP 502 {"error":{"message":"Upstream provider error","type":"upstream_error",
"details":{"error":{"message":"Key limit exceeded (total limit). Manage it using
https://openrouter.ai/workspaces/default/keys/..."}}}}Nothing was wrong with the machine, the install or the config. The fix was a billing page. Before debugging the agent, confirm the key works on its own — test it directly against the provider, outside OpenClaw entirely:
# Anthropic — a minimal request that costs almost nothing
curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-opus-5","max_tokens":16,
"messages":[{"role":"user","content":"say ready"}]}'
# OpenAI-compatible providers (OpenAI, OpenRouter, a gateway):
curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "content-type: application/json" \
-d '{"model":"gpt-4o","max_tokens":16,
"messages":[{"role":"user","content":"say ready"}]}'If that curl fails, no amount of OpenClaw configuration will help. If it succeeds and the agent still cannot answer, the problem is the key, model name or base URL as OpenClaw has them — not the provider.
The web UI does not load right after setup
Caddy terminates TLS and proxies to OpenClaw on its local port, so a failure here is usually one of two things, and they look identical from a browser. Either DNS is not resolving to this server yet, or Caddy is up and the agent behind it is not:
dig +short openclaw.yourdomain.com # must be THIS server's IP
sudo ss -ltnp | grep -E ':80|:443|:18789'
journalctl -u caddy -n 30 --no-pager
curl -I http://127.0.0.1:18789 # does the agent answer locally at all?Point DNS before the first request. Certificate issuance itself is fast — in our provisioning runs Caddy obtained one for a brand-new subdomain in about seven seconds — but querying the hostname too early has a long tail: Cloudflare-hosted zones publish a 30-minute negative-cache TTL, so a “does not exist” answer can outlive the record going live by half an hour. If you hit that, sudo resolvectl flush-caches or wait.
Do not publish the agent's port
OpenClaw should listen on loopback and be reached through Caddy. If you ever run a piece of this stack in Docker, be aware that Docker writes iptables rules evaluated before UFW's: a published container port is reachable from the internet even when ufw status says that port is denied. We measured a database container going from unreachable to publicly open the moment it started, with no firewall rule changed. Bind to 127.0.0.1 and let the proxy be the only thing listening — see our VPS security guide for the full detail.
Prefer not to manage it yourself?
AgentOcean manages OpenClaw on a dedicated VPS — HTTPS, daily backups, monitoring, and one-click updates — without you touching a terminal.