Host OpenClaw
Your always-on personal AI agent, on its own VM and in your Telegram.
Shelley handles the setup. About 15 minutes.
Prefer a terminal?
curl -fsSL https://hub.exe.dev/deploy/openclaw/prompt.txt | ssh exe.dev new --prompt=/dev/stdinOpenClaw (formerly Clawdbot, then Moltbot) is an open-source personal AI agent. Its Gateway is one long-running Node.js process that owns the agent's memory, workspace, tools, and chat connections: you message it from Telegram, WhatsApp, Discord, Slack, or its web dashboard, and it runs commands, edits files, browses, and remembers what you told it, on the machine it lives on.
That last part is why it should not live on your laptop. An exe.dev VM is a
separate Linux machine that is always on: the Gateway runs as a service that
keeps going when you disconnect, the dashboard sits behind exe.dev's login at
https://my-openclaw.exe.xyz/, Claude is reached through exe.dev's
LLM integration so no API key is stored on the VM,
and if the agent breaks something you restore a backup or start over. This
guide sets up exactly that, with a Telegram bot that answers only you.
What you get#
- A persistent agent: the OpenClaw Gateway runs as the systemd user service openclaw-gateway with lingering on, keeps running after you disconnect, and answers with Claude through exe.dev's LLM integration
- One messaging integration: a Telegram bot that replies to the owner's Telegram account and to nobody else (dmPolicy allowlist)
- Service restart: openclaw gateway restart, status, and logs work, and the Gateway comes back by itself after the VM reboots
- Retained configuration: config, credentials, memory, and workspace live in ~/.openclaw, are archived nightly to ~/backups/openclaw, restore from an archive, and survive openclaw update
- The dashboard at https://my-openclaw.exe.xyz/, private behind exe.dev login, with the owner's browser paired
What's running, and where
https://my-openclaw.exe.xyz/
the OpenClaw dashboard, behind exe.dev login and OpenClaw's own device pairing
Deploy with Shelley#
Click Deploy and Shelley will set up OpenClaw on a new VM, then check that it works. No config-file wrangling required.
Have these ready; Shelley asks for them:
TELEGRAM_BOT_TOKEN: the token @BotFather gives you when you send it /newbot in Telegram (looks like 123456789:AA...)
Prefer a terminal?
curl -fsSL \
https://hub.exe.dev/deploy/openclaw/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
TELEGRAM_BOT_TOKEN=<fill-this-in>
Set up OpenClaw on this VM by following the exe.dev recipe at https://hub.exe.dev/deploy/openclaw.md from top to bottom.
The values at the top of this message are mine:
- TELEGRAM_BOT_TOKEN: the token @BotFather gives you when you send it /newbot in Telegram (looks like 123456789:AA...)
Ask me for any that still say <fill-this-in> before you need them.
The recipe's example VM name is my-openclaw. This VM's name is the output of `hostname`: use it wherever the recipe writes my-openclaw. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- A persistent agent: the OpenClaw Gateway runs as the systemd user service openclaw-gateway with lingering on, keeps running after you disconnect, and answers with Claude through exe.dev's LLM integration
- One messaging integration: a Telegram bot that replies to the owner's Telegram account and to nobody else (dmPolicy allowlist)
- Service restart: openclaw gateway restart, status, and logs work, and the Gateway comes back by itself after the VM reboots
- Retained configuration: config, credentials, memory, and workspace live in ~/.openclaw, are archived nightly to ~/backups/openclaw, restore from an archive, and survive openclaw update
- The dashboard at https://my-openclaw.exe.xyz/, private behind exe.dev login, with the owner's browser paired
Never make this VM's web server public (no `share set-public`): the dashboard
controls an agent that has a shell with sudo.
For the Telegram token, run `openclaw channels add` with the token the owner
gave you instead of the `read` line. If they have not created a bot yet,
finish every other section first, then walk them through @BotFather.
Approve a Telegram pairing request only after the owner tells you the code
their bot sent them, then lock the bot to their numeric user ID as shown.
The dashboard pairing link is single-use and expires after ten minutes:
produce it last, after the owner has logged in to https://<vm>.exe.xyz/,
and make a fresh one whenever they ask.
Keep test prompts to the model tiny.
Don't reboot the VM yourself (it would cut off this session): check that
lingering is on and the unit is enabled, and tell the owner how to test a
reboot.
Finish with the URLs I should open.
Also at /deploy/openclaw/prompt.txt. The recipe it follows is /deploy/openclaw.md.
Create the VM#
On your computer, create a VM. Names are global across exe.dev, so if
my-openclaw is taken pick another (or run ssh exe.dev new and use the name
it prints):
ssh exe.dev new --name=my-openclaw
ssh my-openclaw.exe.xyz
Everything below runs on the VM, as the default exedev user.
Install OpenClaw#
OpenClaw needs Node.js 24.16+ or 26.1+, and the VM image ships an older Node.
The official installer takes care of it: on Ubuntu it installs Node 24 LTS from
NodeSource (replacing the system nodejs package), installs OpenClaw with npm
into ~/.npm-global, and puts that on your PATH.
On the VM:
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --no-onboard --no-prompt
source ~/.bashrc
openclaw --version
node --version
It takes two or three minutes. You should see OpenClaw 2026.x.y and
v24.x. --no-onboard skips the interactive setup wizard; the next steps do
the same setup with flags.
Set up the Gateway service#
Non-interactive onboarding writes ~/.openclaw/openclaw.json, generates the
Gateway's auth token, creates the agent workspace, and installs and starts the
systemd user service openclaw-gateway. Models are configured in the next
step, so it skips auth here:
openclaw onboard --non-interactive --accept-risk --mode local \
--auth-choice skip --install-daemon --skip-channels --skip-search
--accept-risk is OpenClaw's acknowledgement that an agent with a shell is
powerful; on a VM of its own, that is the point. The first start of the
Gateway takes about a minute, so onboarding may end with Gateway did not become reachable. That is fine; wait for it:
until curl -fs http://127.0.0.1:18789/healthz; do sleep 5; done; echo
This prints {"ok":true,"status":"live"}.
A systemd user service normally stops when its user's last session ends.
Lingering keeps it running with nobody logged in and starts it at boot. exe.dev
VMs have it on for exedev; make sure:
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" -p Linger
systemctl --user is-enabled openclaw-gateway
Expect Linger=yes and enabled.
Connect Claude through exe.dev#
Every exe.dev VM can call Anthropic's Messages API at
https://llm.int.exe.xyz with no key: exe.dev adds the credentials on the way
out and bills your account. Point OpenClaw's built-in Anthropic provider there:
openclaw config patch --stdin <<'EOF'
{
models: {
providers: {
anthropic: {
baseUrl: "https://llm.int.exe.xyz",
apiKey: "exe-llm-integration",
request: { allowPrivateNetwork: true },
},
},
},
agents: { defaults: { model: { primary: "anthropic/claude-sonnet-5-5" } } },
memory: { search: { provider: "none" } },
}
EOF
openclaw gateway restart
What each line does:
apiKeyis a placeholder. OpenClaw wants a value; the integration ignores it.allowPrivateNetworkis needed becausellm.int.exe.xyzresolves to a link-local address inside the VM, which OpenClaw's SSRF guard blocks unless you opt in for that provider. Without it, every reply fails withBlocked: resolves to private/internal/special-use IP address.- Claude Sonnet 5.5 is the default model;
anthropic/claude-opus-5-5is stronger and costs more (openclaw models set anthropic/claude-opus-5-5). memory.search.provider: "none"makes memory search keyword-based. OpenClaw's semantic memory search wants an OpenAI embeddings key, and the embeddings path doesn't accept the integration's address yet.
The restart takes up to a minute. Then send the agent a test message:
until curl -fs http://127.0.0.1:18789/readyz >/dev/null; do sleep 5; done
openclaw agent --agent main -m "Reply with exactly: pong"
openclaw models status | grep -E 'Default|Utility'
The agent answers pong, and the models are anthropic/claude-sonnet-5-5 and
anthropic/claude-haiku-5-5 (for small background tasks), both through the
integration.
Open the dashboard#
The Gateway listens on 127.0.0.1:18789 only. exe.dev's
HTTPS proxy sends https://my-openclaw.exe.xyz/ to port 8000,
private to you, so nginx on port 8000 passes it to the Gateway, WebSockets
included. This replaces the image's default nginx site:
sudo tee /etc/nginx/sites-available/openclaw >/dev/null <<'EOF'
# https://<vm>.exe.xyz/ (exe.dev proxy) -> nginx :8000 -> OpenClaw Gateway 127.0.0.1:18789
map $http_upgrade $openclaw_connection {
default upgrade;
"" close;
}
server {
listen 8000 default_server;
listen [::]:8000 default_server;
server_name _;
location / {
proxy_pass http://127.0.0.1:18789;
proxy_http_version 1.1;
# The dashboard talks to the Gateway over a WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $openclaw_connection;
proxy_set_header Host $host;
# Overwrite, never append: OpenClaw trusts these only from 127.0.0.1
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 1d;
proxy_send_timeout 1d;
proxy_buffering off;
client_max_body_size 50m;
}
}
EOF
sudo rm -f /etc/nginx/sites-enabled/default
sudo ln -sf /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/openclaw
sudo nginx -t && sudo systemctl reload nginx
Then tell the Gateway its public address and that nginx on 127.0.0.1 is a proxy it can trust for client addresses. The dashboard refuses browser origins it doesn't know, so the address must match exactly:
openclaw config patch --stdin <<EOF
{
gateway: {
publicOrigin: "https://$(hostname).exe.xyz",
controlUi: { allowedOrigins: ["https://$(hostname).exe.xyz"] },
trustedProxies: ["127.0.0.1"],
},
}
EOF
curl -s http://127.0.0.1:8000/healthz; echo
The last line goes through nginx and prints {"ok":true,"status":"live"}.
(Other paths return proxy_attribution_required when you curl them from the VM
itself; that is OpenClaw refusing a forwarded request that claims to come from
localhost, and it is expected.)
Now pair your browser. Two layers protect the dashboard: exe.dev's login, and OpenClaw's own auth, where each browser is a device the Gateway must approve.
-
Open
https://my-openclaw.exe.xyz/and log in to exe.dev. The dashboard asks for a Gateway secret; leave that page open. -
On the VM, make a one-time owner pairing link for this address:
openclaw dashboard --json | jq -r .browserUrl | sed \ -e "s#^http://[^/]*/#https://$(hostname).exe.xyz/#" \ -e "s#gatewayUrl=[^&]*#gatewayUrl=wss%3A%2F%2F$(hostname).exe.xyz#" -
Open the printed
https://my-openclaw.exe.xyz/#bootstrapToken=...link in the same browser within ten minutes. It is single-use; the dashboard connects, and that browser stays paired as the owner.
Type a message in the chat box; Claude answers.
If the link expired, make a new one. To pair another browser or your phone
without a link, run openclaw gateway auth-token --show in a terminal on the
VM (it refuses to print into a pipe or log), paste the token into Gateway
secret, then approve the request it creates:
openclaw devices list
openclaw devices approve <requestId>
Connect Telegram#
Telegram is the easiest channel to run headless: a bot token is the only credential, and OpenClaw long-polls Telegram, so nothing on the VM is opened to the internet.
-
In Telegram, open a chat with @BotFather (check the handle exactly), send
/newbot, choose a display name and a username ending inbot, and copy the token it replies with. -
On the VM, give OpenClaw the token.
read -skeeps it out of your shell history; it is stored in~/.openclaw/openclaw.json, which onlyexedevcan read:read -rsp "Telegram bot token: " TELEGRAM_BOT_TOKEN; echo openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN" unset TELEGRAM_BOT_TOKEN openclaw channels status --probeThe status line for
Telegram defaultshowsenabled, configuredand no error.getMe returned 401means the token is wrong. -
Pair your own account. Send your bot any message. Telegram DMs start in
pairingmode: the bot ignores the message and replies with an 8-character pairing code and your Telegram user ID. On the VM:openclaw pairing list telegram openclaw pairing approve telegram <CODE>Approve only the code your bot sent you. The first approval also makes you the command owner (
commands.ownerAllowFrom), which owner-only commands and approval prompts need. -
Lock the bot to you. In
pairingmode strangers who find the bot can still request codes; an allowlist with your numeric user ID makes it ignore everyone else outright. Set the list before the policy (an empty allowlist is rejected):TG_ID=123456789 # your numeric Telegram user ID, from the bot's pairing reply openclaw config set channels.telegram.allowFrom "[\"$TG_ID\"]" --strict-json openclaw config set channels.telegram.dmPolicy allowlist openclaw config set commands.ownerAllowFrom "[\"telegram:$TG_ID\"]" --strict-json openclaw config get channels.telegram.dmPolicy openclaw config get channels.telegram.allowFromThis prints
allowlistand your ID. The change applies without a restart.
Send your bot another message: it answers within a few seconds. Groups stay
blocked (groupPolicy defaults to allowlist with no groups); see
Telegram access control
if you want the bot in a group.
Restart, status, and logs#
The Gateway is a normal systemd user service; OpenClaw's commands wrap it and add health checks:
openclaw gateway status # Runtime: running, Connectivity probe: ok
openclaw gateway restart # drains running work, restarts, waits for health
openclaw status # channels, model, recent sessions
openclaw logs --follow # the Gateway's own log (also in /tmp/openclaw/)
journalctl --user -u openclaw-gateway -n 100 --no-pager
openclaw doctor # read-only checks; --fix applies repairs
systemctl --user restart openclaw-gateway works too. A restart takes up to a
minute before the dashboard reconnects on its own.
To see it come back after a reboot, reboot the VM (your SSH session drops):
sudo reboot
Give it a minute or two, then on your computer, without logging in to a shell first:
ssh my-openclaw.exe.xyz curl -s http://127.0.0.1:18789/healthz
It prints {"ok":true,"status":"live"}, and your Telegram bot answers again.
The Gateway started at boot because the unit is enabled and lingering is on;
nobody had to log in.
Where your data lives#
OpenClaw keeps everything that matters in ~/.openclaw. The program itself
(~/.npm-global) can always be reinstalled.
| Path | What it holds |
|---|---|
~/.openclaw/openclaw.json |
Config: Gateway token, Telegram bot token, model provider, allowlists (mode 600) |
~/.openclaw/state/openclaw.sqlite |
The Gateway's shared state database |
~/.openclaw/agents/main/agent/ |
The agent's database: sessions, transcripts, auth profiles |
~/.openclaw/workspace/ |
The agent's workspace and memory: AGENTS.md, SOUL.md, USER.md, MEMORY.md, memory/ |
~/.config/systemd/user/openclaw-gateway.service |
The service unit (openclaw gateway install --force rewrites it) |
/etc/nginx/sites-available/openclaw |
The dashboard proxy |
Ask the agent to write lasting preferences into AGENTS.md or MEMORY.md
rather than relying on chat history; those files are what it reads at the start
of every session.
Back up and restore#
Don't copy the live SQLite files; openclaw backup create snapshots them
safely while the Gateway runs and archives config, credentials, and the
workspace with them:
mkdir -p ~/backups/openclaw && chmod 700 ~/backups
openclaw backup create --output ~/backups/openclaw --verify
ls -lh ~/backups/openclaw
A fresh install archives to a few hundred kilobytes, ending in
Archive verification: passed. Make it nightly with a systemd timer that also
deletes archives older than 14 days:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/openclaw-backup.service <<'EOF'
[Unit]
Description=OpenClaw backup archive
[Service]
Type=oneshot
ExecStart=%h/.npm-global/bin/openclaw backup create --output %h/backups/openclaw --verify
ExecStart=/usr/bin/find %h/backups/openclaw -name *-openclaw-backup.tar.gz -mtime +14 -delete
EOF
cat > ~/.config/systemd/user/openclaw-backup.timer <<'EOF'
[Unit]
Description=Nightly OpenClaw backup
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now openclaw-backup.timer
systemctl --user start openclaw-backup.service
systemctl --user list-timers openclaw-backup.timer
The archives hold your bot token, the Gateway token, and every conversation, so keep copies private. A backup on the same disk doesn't survive losing the VM; on your computer, pull copies down now and then:
rsync -av my-openclaw.exe.xyz:backups/openclaw/ ./openclaw-backups/
To restore, unpack an archive into a staging directory, stop the Gateway, swap the state directory, and start again:
ARCHIVE=$(ls -t ~/backups/openclaw/*-openclaw-backup.tar.gz | head -1)
rm -rf ~/openclaw-restore
openclaw backup restore "$ARCHIVE" --target ~/openclaw-restore
systemctl --user stop openclaw-gateway
mv ~/.openclaw ~/.openclaw.before-restore
mv ~/openclaw-restore/*/payload/posix/home/exedev/.openclaw ~/.openclaw
rm -rf ~/openclaw-restore
openclaw doctor --non-interactive
systemctl --user start openclaw-gateway
backup restore verifies the archive before it writes anything, and never
touches the live directory. Once everything works, delete
~/.openclaw.before-restore. To move to a new VM, install OpenClaw there (the
first two sections), copy the archive over, restore it the same way, then rerun
the nginx and gateway config steps from "Open the dashboard" so the address
matches the new VM.
Update#
openclaw update detects the npm install, checks the new version while the
old Gateway keeps serving, then swaps it in, runs openclaw doctor, and
restarts the service. Your ~/.openclaw stays where it is; the update only
replaces the program in ~/.npm-global, and migrates state if a new version
needs it. Take a backup first:
openclaw backup create --output ~/backups/openclaw --verify
openclaw update --dry-run
openclaw update
openclaw --version
openclaw gateway status
Don't use sudo npm i -g openclaw: it installs a second copy outside
~/.npm-global that the service doesn't use. After an update, reload the
dashboard tab; if it reports a protocol mismatch, open a fresh pairing link.
Use your own key or subscription#
The LLM integration is the default because the key never touches the VM. Other options:
-
Your Anthropic API key, still off the VM. Store it in the exe.dev integration instead of on the VM; nothing changes in OpenClaw. On your computer:
printf '%s' "$ANTHROPIC_API_KEY" | ssh exe.dev integrations edit llm --anthropic=byok --anthropic-key=- -
A key on the VM, or another provider. Remove the integration override, then run the interactive onboarding and pick your provider (Anthropic, OpenAI, OpenRouter, and many more):
openclaw config unset models.providers.anthropic openclaw onboard -
A Claude or ChatGPT subscription. Claude Code is preinstalled on the VM; log in with
claude, then choose the Claude CLI route inopenclaw onboardto run the agent on your subscription. For ChatGPT, either connect the subscription to the exe.dev integration (ssh exe.dev integrations setup chatgpt, see LLM integration) or sign in fromopenclaw onboard. Check your plan's terms for automated use first; OpenClaw's Anthropic page explains the trade-offs.
Troubleshooting#
Gateway did not become reachable after onboarding or a restart. The
first start, and each restart, takes about a minute on a small VM. Check
openclaw gateway status; if Runtime isn't running, read
journalctl --user -u openclaw-gateway -n 100 --no-pager. A config error
makes the service exit with status 78 and stay stopped until you fix it;
openclaw doctor names the bad key.
The agent replies OpenClaw couldn't finish this reply. Look in
openclaw logs --follow while you send a message. Blocked: resolves to private/internal/special-use IP address means request.allowPrivateNetwork
is missing from models.providers.anthropic. A 401 or 403 from
llm.int.exe.xyz means the LLM integration isn't attached to this VM; test it
with
curl -s https://llm.int.exe.xyz/v1/messages -H 'content-type: application/json' -H 'anthropic-version: 2023-06-01' -d '{"model":"claude-haiku-4-5","max_tokens":5,"messages":[{"role":"user","content":"hi"}]}'
and see LLM integration.
The dashboard says Browser origin not allowed. allowedOrigins must be
exactly https://<vm>.exe.xyz, with no trailing slash. If the VM was renamed
or this is a copy, rerun the gateway config patch above.
The dashboard shows pairing required or Pairing link is no longer valid. The link was used or is older than ten minutes. Make a new one, or
approve the browser with openclaw devices list and openclaw devices approve <requestId>. Private windows forget their pairing when closed.
502 Bad Gateway at https://my-openclaw.exe.xyz/. nginx is up but the
Gateway isn't (often mid-restart). openclaw gateway status. If you see
exe.dev's welcome page or a 404 instead, the default nginx site is still
enabled: ls /etc/nginx/sites-enabled/.
proxy_attribution_required. gateway.trustedProxies is missing
127.0.0.1, or you curled nginx from the VM itself, which is expected to fail;
use /healthz for local checks.
The Telegram bot doesn't answer. openclaw channels status --probe shows
the channel's state and last error. getMe returned 401 means a wrong or
revoked token: get a new one from @BotFather with /token and run
openclaw channels add --channel telegram --token ... again. If the bot is
silent for you but working, check that channels.telegram.allowFrom holds
your numeric user ID (not your @username).
openclaw: command not found. The installer added ~/.npm-global/bin to
~/.bashrc; run source ~/.bashrc or open a new SSH session.
Node ... unsupported. node --version must be 24.16+ or 26.1+. Rerun the
installer, which repairs Node, then openclaw doctor --fix.
Frequently asked questions#
Do I need an Anthropic or OpenAI API key to run OpenClaw on exe.dev?
No. This setup points OpenClaw's Anthropic provider at exe.dev's LLM integration, which adds the credentials outside the VM, so no key is stored where the agent can read it. Usage is billed to your exe.dev account. You can switch the integration to your own Anthropic key, or use a key or subscription directly; see "Use your own key or subscription" above.
How much does it cost to host OpenClaw on exe.dev?
The VM is part of your exe.dev plan (pricing); the Gateway needs about 1 GB of memory. Model calls are the variable part: besides your messages, OpenClaw wakes the agent for a heartbeat every 30 minutes and runs a nightly memory pass. openclaw config set agents.defaults.heartbeat.every 2h makes the heartbeat rarer, and 0m turns it off.
Is the OpenClaw dashboard exposed to the internet?
No. The Gateway listens on 127.0.0.1 only, and nginx hands it to exe.dev's proxy on port 8000, which lets through only people who are logged in to exe.dev and have access to the VM. OpenClaw then still requires its own token or a paired browser. Don't run share set-public on this VM.
Is OpenClaw the same as Clawdbot and Moltbot?
Yes. The project was called Clawdbot, then Moltbot, and is now OpenClaw; the command is openclaw and its data lives in ~/.openclaw. The installer and openclaw doctor migrate older installs.
Can I use WhatsApp, Discord, or Slack instead of Telegram?
Yes. openclaw channels add sets up any of them next to or instead of Telegram, and the same pairing and allowlist rules apply; see OpenClaw channels. Telegram is used here because it needs nothing but a bot token and makes only outbound connections.
What can the agent do on the VM?
Anything the exedev user can, and that user has passwordless sudo. That is the reason to give OpenClaw its own VM: keep other secrets and projects off it, back it up, and if it breaks something, restore or start a fresh VM. See OpenClaw's security guide for sandboxing and tool policies.
Does OpenClaw keep running when I close my laptop?
Yes. exe.dev VMs stay on, and the Gateway is a systemd service that does not depend on your SSH session. It restarts if it crashes and starts again after a reboot.
Updated Oct 14, 2026 · Markdown · manifest.json · Something wrong? Tell us