Overview
OpenClaw is an agentic AI runtime that allows you to run AI assistants via Telegram and other chat channels. This guide covers the entire process from configuring your VPS, installing Ollama for local LLMs, to connecting OpenClaw with Telegram – ensuring a stable and secure production environment. For a related self-hosted workflow-automation setup, see Local LLM + n8n: Building Cost-Free Automation Workflows.
Stack Covered in This Guide:
- Ubuntu 24.04 LTS
- Node.js 22+
- Ollama (local LLM inference)
- OpenClaw 2026.3.x
- Telegram Bot
- Google Gemini CLI (primary cloud model)
graph TD
User([User via Telegram]) -->|Internet| NG[Nginx Reverse Proxy: 443]
NG -->|Localhost Auth| GW[OpenClaw Gateway: 18789]
GW -->|Primary Inference| GM[Cloud: Google Gemini API]
GW -->|Fallback Inference| OL[Local: Ollama Service 11434]
style NG fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff
style GW fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style OL fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff
Part 1: Preparing the VPS
1.1 Update the System
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git nano python3 ufw fail2ban
1.2 Create a Dedicated User for OpenClaw
Do not run OpenClaw as root. Create a dedicated user named claw:
sudo adduser claw
This command will prompt you for some information. Set a strong password for claw – write it down, as you'll need it for the first sudo command. Fields like Full Name can be left blank.
sudo usermod -aG sudo claw
su - claw
Note: From this point forward, all commands should be run under the
clawuser. Commands that require root privileges will usesudo.
1.3 Fix the Hostname (To Avoid Sudo Errors)
Ensure the hostname is resolved correctly to prevent unable to resolve host warnings:
# Get the current hostname
hostname
# Add it to /etc/hosts if it isn't there already
sudo bash -c 'echo "127.0.1.1 $(hostname)" >> /etc/hosts'
Verify:
grep "$(hostname)" /etc/hosts
# You should see a line reading "127.0.1.1 <your-hostname>" in the output
1.4 Configure the Firewall (UFW)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status
Expected output:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
1.5 Generate an SSH Key (required before disabling password login)
Perform this step on your local machine, not on the VPS:
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/id_openclaw_vps
# Use a specific key name so you can tell it apart from keys for other servers
# Set a passphrase, or leave it blank
Copy the public key to the VPS:
ssh-copy-id -i ~/.ssh/id_openclaw_vps.pub claw@<VPS_IP>
# Enter the password one last time
Or copy it manually if ssh-copy-id isn't available:
# On your local machine – print the public key
cat ~/.ssh/id_openclaw_vps.pub
# On the VPS – paste it into authorized_keys
mkdir -p /home/claw/.ssh
chmod 700 /home/claw/.ssh
nano /home/claw/.ssh/authorized_keys
# Paste the public key. Save and exit: Ctrl+X → Y → Enter
chmod 600 /home/claw/.ssh/authorized_keys
Verify key-based login before disabling password auth – open a new terminal and test:
ssh -i ~/.ssh/id_openclaw_vps claw@<VPS_IP>
# If the key has a passphrase: it will ask for the key's passphrase (not the server password)
# If the key has no passphrase: you'll log in directly with no prompt
# Either way, NOT being asked for the server password means it worked
Important: Don't close your current terminal while testing. If the new login fails, you still have the existing session to fix it from.
1.6 Secure SSH
⚠️ Only do this after you have successfully verified SSH key login.
sudo nano /etc/ssh/sshd_config
Ensure the following lines are set:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
sudo systemctl restart ssh
Verify from a new terminal – open another window and SSH in again without closing your current session. If the new login succeeds, you're done; if not, use the existing session to revert the change.
1.7 Configure Fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
1.8 Add Swap Space (Crucial for 8GB VPS)
Setting up a 2GB swap acts as a safety net to prevent sudden crashes when OpenClaw runs alongside Ollama and experiences short-term RAM spikes.
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
The last line adds the swap file to /etc/fstab so it's re-enabled automatically after a VPS reboot.
Verify:
free -h
# Swap should show 2GB
Part 2: Install Node.js
Use Node.js 22.x – this is the current LTS version.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node --version # Must be >= 22
npm --version
Part 3: Install Ollama
3.1 Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
3.2 Configure the Ollama Service
export EDITOR=nano
sudo systemctl edit ollama
Add the following content:
[Service]
Environment="OLLAMA_NUM_PARALLEL=1"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_NUM_THREAD=2"
Save and exit: Ctrl+X → Y → Enter
OLLAMA_NUM_THREAD=2caps Ollama at 2 CPU cores, leaving headroom for OpenClaw and the OS. Adjust based on how many cores your VPS actually has.
sudo systemctl daemon-reload
sudo systemctl enable ollama
sudo systemctl start ollama
3.3 Pull the Appropriate Model
We are using qwen3:1.7b-q8_0 in this guide – the strongest quantization of the Qwen3 1.7B model that still comfortably fits into 8GB of RAM, and is plenty for everyday tasks like summarization, drafting content, and workflow routing.
Pick the version that fits your available RAM (check the "available" column from free -h):
| Tag | RAM Needed | When to Use |
|---|---|---|
| qwen3:1.7b-q8_0 | ~3GB | Available > 4GB – recommended |
| qwen3:1.7b-q4_K_M | ~1.5GB | Available 2–4GB |
See the full model comparison table in Part 11 if you want to pick a different model that better fits your VPS specs.
Check RAM before pulling:
free -h
# Check the "available" column
Pull the model:
ollama pull qwen3:1.7b-q8_0
Verify:
ollama list
Expected output:
NAME ID SIZE MODIFIED
qwen3:1.7b-q8_0 29b6a7a420a4 2.2 GB X seconds ago
ollama run qwen3:1.7b-q8_0 "hello"
Expected output:
Hello! How can I help you today?
The model has a thinking mode – you'll see a
Thinking...line before the actual reply. This is normal.
Part 4: Install Google Gemini CLI
OpenClaw uses the Gemini CLI for cloud inference – this is the primary model in this setup, far faster than a local LLM running on CPU alone.
sudo npm install -g @google/gemini-cli
Fix permissions on the config directory (needed because it was installed with sudo):
sudo chown -R claw:claw /home/claw/.gemini
Verify:
gemini --version
# Should print a version with no error, e.g. 0.35.3
Log in with your Google account:
gemini
This opens the Gemini CLI's interactive mode. Follow these steps:
Step 1 – Trust the folder
Do you trust the files in this folder?
1. Trust folder (claw) ← select this
2. Trust parent folder (home)
3. Don't trust
Select 1. Trust folder (claw).
Step 2 – Choose the auth method
How would you like to authenticate?
1. Sign in with Google ← select this
2. Use Gemini API Key
3. Vertex AI
Select 1. Sign in with Google. The Gemini CLI will print a URL – copy it, open it on your local machine, log into Google, then paste the authorization code back into the VPS terminal.
Step 3 – Confirm you're signed in
A successful auth shows:
Signed in with Google: your@gmail.com
Exit the CLI with Ctrl+D. If that doesn't work, open a new SSH terminal and kill the process:
ps aux | grep gemini
kill <PID>
Verify auth succeeded using non-interactive mode:
gemini -p "hello"
Note:
/authis a slash command used to change the auth method inside a running session – you don't need it for first-time setup.
Part 5: Install OpenClaw
5.1 Install OpenClaw CLI
sudo npm install -g openclaw
5.2 Run Onboarding
openclaw onboard
Step 1 – Confirm personal use
I understand this is personal-by-default and shared/multi-user use requires lock-down. Continue?
Select Yes.
Step 2 – Setup mode
Setup mode
● QuickStart (Configure details later via openclaw configure.)
○ Manual
Select QuickStart – settings can be adjusted later with openclaw configure.
Step 3 – Model/auth provider
Model/auth provider
● Anthropic (Claude CLI + setup-token + API key)
○ ...
○ Ollama
○ Google
○ Skip for now
Select Skip for now – Gemini CLI and Ollama will be configured separately in Part 6.
Step 4 – Filter models by provider
Filter models by provider
● All providers
○ google-gemini-cli
○ ollama
○ ...
Select google-gemini-cli.
Step 5 – Model check
Model check
No auth configured for provider "google-gemini-cli". The agent may fail until
credentials are added. Run `openclaw models auth login --provider google-gemini-cli`,
`openclaw configure`, or set an API key env var.
Expected warning – credentials will be linked in Part 6.
Step 6 – Select channel
Select channel (QuickStart)
Telegram (Bot API)
Select Telegram (Bot API).
Step 7 – Provide bot token
How do you want to provide this Telegram bot token?
Enter Telegram bot token
Select Enter Telegram bot token. Obtain a token from BotFather:
- Open Telegram, search
@BotFather - Send
/newbot - Set a display name (e.g.,
OpenClaw) - Set a username (e.g.,
openclawbot) – must be unique across Telegram - BotFather returns a token in the format
123456789:ABC-DEF...– copy the full string
Telegram DM access: The bot defaults to
pairingDM policy – anyone can send a pairing request, but approval is required viaopenclaw pairing approve telegram <code>. To restrict access to yourself only:# Get your Telegram user ID at: https://t.me/userinfobot openclaw config set channels.telegram.dmPolicy "allowlist" openclaw config set channels.telegram.allowFrom '["YOUR_USER_ID"]'
Step 8 – Web search provider
Search provider
● DuckDuckGo Search (experimental)
○ Brave Search
○ Exa Search
○ Skip for now
Select DuckDuckGo Search (experimental) – free, no API key required.
Step 9 – Optional API keys
Select No for all optional prompts – these can be configured later:
Set GOOGLE_PLACES_API_KEY for goplaces? → No
Set NOTION_API_KEY for notion? → No
Set OPENAI_API_KEY for openai-whisper? → No
Set ELEVENLABS_API_KEY for sag? → No
Step 10 – Onboarding completes automatically
The wizard performs the following without requiring input:
- Saves config to
~/.openclaw/openclaw.json - Enables systemd lingering for the
clawuser (keeps gateway running after logout) - Installs the gateway service at
~/.config/systemd/user/openclaw-gateway.service - Starts the gateway and verifies Telegram connectivity
Expected output:
Telegram: ok (@openclawbot)
Agents: main (default)
Step 11 – Hatch the bot
How do you want to hatch your bot?
● Hatch in TUI (recommended)
○ Open the Web UI
○ Do this later
Select Hatch in TUI. The wizard launches the TUI and sends an initial message to the bot. An error will appear:
⚠️ Agent failed before reply: No API key found for provider "google-gemini-cli".
This is expected – OpenClaw has not yet been linked to Gemini CLI credentials. Exit the TUI with Ctrl+C and proceed to Part 6.
Part 6: Model Configuration
6.1 Add the Ollama provider
openclaw models auth add
- Token provider:
custom - Provider id:
ollama - Profile id:
ollama:local - Does this token expire?:
No - Paste token:
ollama
6.2 Fix Auth for Gemini CLI
After onboarding, google-gemini-cli will show Auth = no. Fix it with:
openclaw models auth login --provider google-gemini-cli
The wizard shows a caution prompt:
Google Gemini CLI caution
This is an unofficial integration and is not endorsed by Google.
Some users have reported account restrictions or suspensions...
Select Yes to continue.
OpenClaw stores its own credentials separately from the Gemini CLI, so you need to authenticate again here.
The wizard prints a URL:
Gemini CLI OAuth
You are running in a remote/VPS environment.
A URL will be shown for you to open in your LOCAL browser.
After signing in, copy the redirect URL and paste it back here.
Open this URL in your LOCAL browser:
https://accounts.google.com/o/oauth2/v2/auth?...
Copy that URL, open it on your local machine, and log into Google. After signing in, the browser redirects to a new URL – copy that entire URL and paste it into the VPS terminal.
Verify:
openclaw models list
# The Auth column for google-gemini-cli should show "yes"
6.3 Strategic Model Fallbacks
openclaw models set google-gemini-cli/gemini-2.5-flash
openclaw models fallbacks add ollama/qwen3:1.7b-q8_0
Note: If you have a VPS with 16GB RAM and a strong CPU, you can set Ollama as primary to save API costs. With 8GB RAM / 4 CPU, Gemini primary is the most practical choice.
6.4 Add Google Pro for Heavy Research Execution
# Already added after onboarding, verify with:
openclaw models list
When you need heavier research, select it directly in-session:
/model google-gemini-cli/gemini-2.5-pro-preview
6.5 Verify Configuration
openclaw models list
Expected output:
Model Input Ctx Local Auth Tags
google-gemini-cli/gemini-2.5-flash text+image 1024k no yes default,configured
ollama/qwen3:1.7b-q8_0 text 40k yes yes fallback#1,configured
google-gemini-cli/gemini-2.5-pro-preview text+image 1024k no yes configured
Part 7: Tools Profile Setup
Drop processing complexities by using the lightweight tools profile:
openclaw config set tools.profile minimal
Part 8: Initiating the Gateway Layer
8.1 Start the Gateway
openclaw gateway restart
8.2 Check Health
openclaw health
Expected output:
Telegram: ok (@yourbotname)
Agents: main (default)
8.3 Test on Telegram
Open Telegram and message "hello" to the bot. Since the DM policy is pairing, the bot replies with a pairing code:
OpenClaw: access not configured.
Your Telegram user id: 1575431582
Pairing code: {KEY}
Ask the bot owner to approve with:
openclaw pairing approve telegram {KEY}
Approve it on the VPS to start chatting:
openclaw pairing approve telegram {KEY}
After approving, message the bot again – it will reply normally. You only need to approve once.
Part 9: Expose Web UI via Subdomain
The Web UI runs only on 127.0.0.1:18789 by default. This part covers exposing it externally through a subdomain (e.g., claw.your-domain.com) with HTTPS.
9.1 Address Configuration Resolution
In your DNS provider, add an A record:
claw.your-domain.com → <VPS_IP>
Wait for DNS to propagate (usually a few minutes, up to 24h). Verify:
dig claw.your-domain.com +short
# Should return the VPS IP
9.2 Gateway Firewall Exceptions
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
9.3 Implement Nginx + Certbot
sudo apt install -y nginx certbot python3-certbot-nginx
sudo nano /etc/nginx/sites-available/openclaw
server {
listen 80;
server_name claw.your-domain.com;
location / {
proxy_pass http://127.0.0.1:18789;
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;
}
}
sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
sudo nginx -t
# Should show: syntax is ok + test is successful
sudo systemctl reload nginx
9.4 Enable SSL
sudo certbot --nginx -d claw.your-domain.com
Certbot will prompt for:
- Email – to receive notifications when the cert is about to expire
- Terms of Service – enter
yto agree - Share email with EFF – enter
yorn, your choice
Expected output:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/claw.your-domain.com/fullchain.pem
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Congratulations! You have successfully enabled HTTPS on https://claw.your-domain.com
The certificate is valid for 90 days. Certbot renews it automatically in the background – no action needed.
Verify auto-renew is running:
sudo systemctl status certbot.timer
Expected output:
● certbot.timer - Run certbot twice daily
Loaded: loaded (/usr/lib/systemd/system/certbot.timer; enabled; preset: enabled)
Active: active (waiting)
Trigger: ...
Triggers: ● certbot.service
active (waiting) is correct – the cert renews twice a day.
9.5 Gateway Enhancements
openclaw config set gateway.controlUi.allowedOrigins '["https://claw.your-domain.com"]'
openclaw config set gateway.trustedProxies '["127.0.0.1"]'
openclaw config set gateway.auth.mode "token"
openclaw gateway restart
Retrieve the authorized UI URL:
python3 -c "import json; d=json.load(open('/home/claw/.openclaw/openclaw.json')); print('https://claw.your-domain.com/#token=' + d['gateway']['auth']['token'])"
If it still throws a pairing required loop over the proxy, switch to Basic Auth.
9.6 Nginx Basic Auth (Optional Fallback)
If token-based auth produces a pairing required loop through Nginx, disable it and use HTTP Basic Auth instead:
openclaw config set gateway.auth.mode "none"
openclaw gateway restart
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd your-username
Edit /etc/nginx/sites-available/openclaw and add these two lines inside the location / block:
auth_basic "OpenClaw";
auth_basic_user_file /etc/nginx/.htpasswd;
sudo nginx -t
sudo systemctl reload nginx
9.7 Fail2ban for Nginx
Block brute-force attempts against Basic Auth:
sudo nano /etc/fail2ban/jail.local
Add:
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-http-auth
After 5 failed password attempts, the source IP is banned for 1 hour.
9.8 Accessing the Web UI
Open https://claw.your-domain.com in a browser. The browser will prompt for Basic Auth credentials.
After passing Basic Auth, leave both the Gateway Token and Password fields empty and click Connect.
Note: Basic Auth has no session timeout – the browser caches credentials until it is fully closed or the cache is cleared. Use an Incognito/Private window if you want to be prompted each session.
Part 10: Monitoring & Troubleshooting
Real-time Logs
openclaw logs --follow
Monitor Hardware
free -h
# "available" should stay above 2GB while Ollama has a model loaded
Check Ollama Inference
ollama ps
# Shows the loaded model and its current CPU usage %
Check CPU Load
top -bn1 | head -5
# Load average shouldn't exceed the number of CPU cores
Session System Wipes
echo '{}' > ~/.openclaw/agents/main/sessions/sessions.json
rm -f ~/.openclaw/agents/main/sessions/*.jsonl
openclaw gateway restart
Common Errors
| Error | Cause | Fix |
|---|---|---|
| OOM: model requires X GiB | Model too large for available RAM | Use a smaller quantization or smaller model |
| typing TTL reached (2m) | CPU inference too slow | Switch Gemini to primary |
| 404 model_not_found | Incorrect model name | Verify with openclaw models list |
| 429 No capacity | Google API overload | Wait or switch to Ollama |
| [xai-auth] bootstrap config fallback: no config-backed key found | OpenClaw looking for xAI/Grok config | Safe to ignore if xAI is not in use |
Part 11: Decisive AI Model Selections
| Model | Quant | Size | Required RAM | Vietnamese | Tool Calling | Sweet Spot | |---|---|---|---|---|---|---| | qwen3:1.7b | q4_K_M | ~1.5GB | ~2GB | Good | Fair | VPS 8GB + additional heavy services | | qwen3:1.7b | q8_0 | ~2.3GB | ~3GB | Good | Fair | VPS 8GB (general use) | | qwen2.5:3b-instruct | q4_K_M | ~1.9GB | ~2.5GB | Good | Good | Solid sweet spot for 8GB nodes | | qwen2.5:3b-instruct | q8_0 | ~3.3GB | ~4GB | Good | Good | VPS 16GB | | qwen2.5:7b-instruct | q4_K_M | ~5.2GB | ~6.5GB | Good | Good | VPS 16GB Dedicated |
On an 8GB VPS with OpenClaw active (~4.5–5GB RAM), the safest options are
qwen2.5:3b-instruct-q4_K_Morqwen3:1.7b-q8_0.
Conclusion
This setup runs on 3 tiers:
- Primary – Gemini 2.5 Flash (cloud, fast, 1024k context)
- Fallback – Ollama local (offline-capable, zero-cost)
- On-demand – Gemini Pro for explicit heavy research queries
Guide written based on empirical OpenClaw deployment operations inside 4 CPU / 8GB RAM instances.