# ๐Ÿฆ… Setting Up OpenClaw: Self-Hosted AI Gateway on a VM (Telegram + Web)

**Date:** July 16, 2026  
**Tags:** AI, OpenClaw, Self-Hosted, Systemd, Nginx, Telegram, AI Agents, Reverse Proxy

---

## ๐ŸŽฏ Overview

[OpenClaw](https://github.com/openclaw/openclaw) is a self-hosted **AI agent gateway** that exposes your models and agents over a clean HTTP API and over chat platforms like **Telegram**. Think of it as the "front door" for your AI stack: it brokers requests between chat clients and whatever model backend you point it at (local LLMs, OpenAI-compatible routers like [9Router](/blog/9router-setup/), or cloud providers).

Where [Hermes](/blog/hermes-setup/) is the *agent brain* (tool use, scheduling, memory), OpenClaw is the *gateway* โ€” it answers the question "how do I actually talk to my agents from Telegram or a web client without leaking infra details?"

### Why self-host OpenClaw?

| Feature | Benefit |
|---------|---------|
| **๐Ÿ”’ Privacy First** | Your chat traffic and agent calls stay on your VM |
| **๐Ÿ“ก Multi-Platform** | Telegram bot + HTTP API from one gateway |
| **๐ŸŒ Reverse Proxy** | HTTPS termination via nginx, no exposed ports |
| **๐Ÿ”Œ Pluggable** | Swap model backends without touching clients |
| **๐Ÿ› ๏ธ Systemd Native** | Survives reboots, supervised, auto-restart |
| **๐Ÿงฉ 9Router Ready** | Point it at a free model router in one line |

---

## ๐Ÿ—๏ธ Architecture Overview

```
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚              Your VM (Linux)             โ”‚
                 โ”‚                                           โ”‚
   Telegram โ”€โ”€โ”€โ–บ โ”‚  openclaw-gateway.service  (port 18790)  โ”‚
   (bot API)     โ”‚        โ”‚                                  โ”‚
                 โ”‚        โ”‚ forwards to model backend        โ”‚
                 โ”‚        โ–ผ                                  โ”‚
                 โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚
   Browser โ”€โ”€โ”€โ”€โ”€โ–บโ”‚  โ”‚ openclaw-https-proxy     โ”‚โ—„โ”€โ”€ nginx    โ”‚
   (HTTPS)       โ”‚  โ”‚ (port 18789 -> 18790)    โ”‚            โ”‚
                 โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚
                 โ”‚        โ”‚                                  โ”‚
                 โ”‚        โ–ผ                                  โ”‚
                 โ”‚  9Router / OpenAI-compatible models       โ”‚
                 โ”‚  (e.g. http://192.168.51.115:20128/v1)    โ”‚
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

The gateway listens on an internal port. An nginx-based HTTPS proxy sits in front of it so external clients (and Telegram webhooks) hit a standard `https://` endpoint instead of a raw LAN port.

---

## ๐Ÿ“‹ Prerequisites

Before you start, make sure you have:

- A Linux VM (this guide assumes Debian/Ubuntu or similar systemd-based distro).
- A non-root user with `sudo` (we use `ryan` in examples).
- `node` + `npm` available (OpenClaw ships as a Node package).
- A domain or LAN IP for the gateway (we use `192.168.51.115` internally).
- A Telegram bot token from [@BotFather](https://t.me/BotFather) (placeholder below).
- A model backend โ€” we point OpenClaw at **9Router** (see the [9Router guide](/blog/9router-setup/)).

> โš ๏ธ **Security note:** Never commit real tokens or API keys. Every secret in this guide is a `` you replace locally.

---

## ๐Ÿ“ฆ Step 1 โ€” Install OpenClaw

Install OpenClaw globally via npm (or via your package manager of choice):

```bash
# Install OpenClaw globally
npm install -g openclaw

# Verify it landed
which openclaw
# -> /home/ryan/.local/bin/openclaw (or similar)

# Check the version
openclaw --version
```

If you prefer a pinned version, replace `openclaw` with `openclaw@` (e.g. `openclaw@2026.6.11`).

---

## โš™๏ธ Step 2 โ€” Configure the Gateway

OpenClaw reads its config from a config file (location depends on install; commonly `~/.config/openclaw/` or the working directory). Below is a **sanitized** example โ€” every secret is a placeholder.

```yaml
# ~/.config/openclaw/config.yaml (sanitized)

gateway:
  host: 0.0.0.0
  port: 18790
  # Public base URL used in links/telegram webhook callbacks:
  public_url: "https://"

telegram:
  # Bot token from @BotFather โ€” DO NOT commit the real value
  bot_token: ""
  # Optional: restrict to your chat id
  allowed_chat_ids:
    - ""

models:
  # Point OpenClaw at your 9Router (or any OpenAI-compatible endpoint)
  default_provider: openai-compatible
  openai_compatible:
    base_url: "http://192.168.51.115:20128/v1"
    # API key for 9Router โ€” placeholder only
    api_key: ""
    default_model: "Free"

auth:
  # Shared desktop pairing token (used by the OpenClaw Desktop app)
  session_token: ""
```

> ๐Ÿ’ก **Tip:** Keep secrets in a separate env file and reference them, rather than inlining them in `config.yaml`. OpenClaw supports env interpolation in most setups.

---

## ๐Ÿš€ Step 3 โ€” Create the systemd Service

Running the gateway as a systemd user service means it restarts on boot and recovers from crashes. Create the unit file:

```ini
# ~/.config/systemd/user/openclaw-gateway.service
[Unit]
Description=OpenClaw Gateway (v2026.6.11)
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=/home/ryan/.hermes/node/bin/node /home/ryan/.hermes/node/lib/node_modules/openclaw/dist/index.js gateway --port 18790
WorkingDirectory=/home/ryan
Environment=NODE_ENV=production
Restart=always
RestartSec=5

[Install]
WantedBy=default.target
```

Then enable and start it:

```bash
# Reload systemd so it picks up the new unit
systemctl --user daemon-reload

# Enable on boot (lingering session required for user services at boot)
systemctl --user enable openclaw-gateway.service

# Start it now
systemctl --user start openclaw-gateway.service

# Check it is alive
systemctl --user is-active openclaw-gateway.service
# -> active
```

> ๐Ÿ”ง **Gotcha โ€” "lingering" user services:** If the gateway should start at boot *without* you logged in, enable lingering:
> ```bash
> loginctl enable-linger $USER
> ```

---

## ๐ŸŒ Step 4 โ€” HTTPS Reverse Proxy (nginx)

Exposing port `18790` directly is bad practice. Put an nginx proxy in front so clients use HTTPS. The proxy listens on `18789` and forwards to the gateway on `18790`.

```nginx
# /home/ryan/.config/openclaw-proxy/nginx.conf (sanitized)
worker_processes 1;

daemon off;

events { worker_connections 1024; }

http {
  upstream openclaw {
    server 127.0.0.1:18790;
  }

  server {
    listen 18789 ssl;
    server_name ;

    ssl_certificate     /etc/ssl/openclaw/fullchain.pem;
    ssl_certificate_key /etc/ssl/openclaw/privkey.pem;

    location / {
      proxy_pass http://openclaw;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    }
  }
}
```

Run the proxy as its own service:

```ini
# ~/.config/systemd/user/openclaw-https-proxy.service
[Unit]
Description=OpenClaw HTTPS Proxy on port 18789
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/sbin/nginx -c /home/ryan/.config/openclaw-proxy/nginx.conf -g daemon off;
Restart=always
RestartSec=5

[Install]
WantedBy=default.target
```

```bash
systemctl --user daemon-reload
systemctl --user enable --now openclaw-https-proxy.service
systemctl --user is-active openclaw-https-proxy.service
# -> active
```

---

## ๐Ÿค– Step 5 โ€” Wire Up the Telegram Bot

1. Message [@BotFather](https://t.me/BotFather) on Telegram.
2. Send `/newbot` and follow prompts to get a **bot token**.
3. Paste the token into `config.yaml` as `telegram.bot_token` (placeholder ``).
4. (Optional) Restrict who can talk to the bot via `allowed_chat_ids`.
5. Restart the gateway:

```bash
systemctl --user restart openclaw-gateway.service
```

6. Open a chat with your bot and send `/start`. If the gateway is healthy you'll get a reply.

> ๐Ÿ” **Health check:** Most OpenClaw builds expose a `/health` endpoint. Verify it:
> ```bash
> curl -s --max-time 8 http://127.0.0.1:18790/health
> # -> {"ok":true,"status":"live"}
> ```

---

## ๐Ÿ”Œ Step 6 โ€” Point OpenClaw at 9Router (Free Models)

OpenClaw needs a model backend. We use **9Router** (see the [9Router guide](/blog/9router-setup/)), our LAN OpenAI-compatible router that auto-rotates through free models. The relevant config is the `models.openai_compatible` block from Step 2:

```yaml
models:
  default_provider: openai-compatible
  openai_compatible:
    base_url: "http://192.168.51.115:20128/v1"
    api_key: ""
    default_model: "Free"
```

Test that the gateway can reach 9Router:

```bash
# List models 9Router serves (key is a placeholder)
curl -s --max-time 8 -H "Authorization: Bearer " \
  http://192.168.51.115:20128/v1/models | head -c 400
```

If you see a JSON `data` array, the wiring is good.

---

## ๐Ÿงช Step 7 โ€” Smoke Test the Full Stack

Run through this checklist after setup:

```bash
# 1. Gateway process is up
systemctl --user is-active openclaw-gateway.service

# 2. Proxy is up
systemctl --user is-active openclaw-https-proxy.service

# 3. Gateway health endpoint
curl -s --max-time 8 http://127.0.0.1:18790/health

# 4. HTTPS proxy forwards correctly
curl -sk --max-time 8 https://127.0.0.1:18789/health

# 5. Telegram bot replies (send /start in the app, watch logs)
journalctl --user -u openclaw-gateway.service -f
```

Expected healthy output:

```text
openclaw-gateway.service   active
openclaw-https-proxy.service  active
{"ok":true,"status":"live"}
{"ok":true,"status":"live"}
```

---

## ๐Ÿ› ๏ธ Operations & Troubleshooting

### Restarting

```bash
# Restart just the gateway
systemctl --user restart openclaw-gateway.service

# Restart just the proxy
systemctl --user restart openclaw-https-proxy.service

# Restart both
systemctl --user restart openclaw-gateway.service openclaw-https-proxy.service
```

### Common failure modes

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| Gateway won't start | Bad `config.yaml` syntax | `openclaw gateway --port 18790` in foreground to see errors |
| Telegram bot silent | Wrong `bot_token` / not restarted | Re-paste ``, restart service |
| `502` from proxy | Gateway down or wrong upstream port | Check `is-active`, confirm upstream `127.0.0.1:18790` |
| Model calls fail | 9Router key/endpoint wrong | `curl` the `/v1/models` endpoint directly |
| Health returns 404 | Endpoint path differs by build | Check docs for your version |

> ๐Ÿฉน **Real-world note:** We hit a SQLite plugin-index corruption that took the gateway down. The recovery writeup lives here: [OpenClaw Gateway Down: SQLite Plugin Conflict & Recovery](/blog/openclaw-gateway-sqlite-plugin-conflict/).

---

## ๐Ÿ” Security Hardening Checklist

- [ ] Bot token stored as `` placeholder, never committed.
- [ ] `allowed_chat_ids` set so strangers can't drive your agents.
- [ ] Gateway bound to `127.0.0.1` or LAN only; internet exposure via nginx + TLS.
- [ ] TLS certs in a restricted dir (`chmod 600` private key).
- [ ] `loginctl enable-linger` only if you truly need boot-time start without login.
- [ ] Rotate `` periodically.

---

## โœ… Summary

You now have a self-hosted **OpenClaw gateway** that:

- Runs as supervised **systemd user services** (`openclaw-gateway.service` + `openclaw-https-proxy.service`).
- Terminates **HTTPS** via nginx on `18789` and forwards to the gateway on `18790`.
- Talks to **Telegram** through a bot wired with a placeholder token.
- Routes model calls through **9Router** for free, OpenAI-compatible inference.
- Exposes a `/health` endpoint for monitoring.

From here you can add more agents, swap model backends, or layer on auth โ€” all without touching the clients. ๐Ÿš€

---

*No API keys, tokens, or secrets are included in this guide โ€” every value is a `` you supply locally. Built with Astro, GitHub, and Cloudflare Pages.*