Skip to content

How to Secure OpenCLAW on a VPS with Gateway Configuration

I nearly fell off my chair when I saw this on Reddit. Someone discovered that their OpenCLAW agent was completely exposed to the internet - and they had email and calendar integrations enabled.

Let me check my own setup immediately.

The Wake-Up Call

I ran the diagnostic command on my VPS:

check-openclaw-exposure.sh
openclaw config get | grep host

Output:

Terminal output
host: 0.0.0.0

My heart sank. 0.0.0.0 means my OpenCLAW gateway was listening on all network interfaces. Anyone who knew my VPS IP address could potentially send messages to my agent.

Let me verify what that actually means:

check-listening-ports.sh
sudo netstat -tulpn | grep 18789

Output:

Netstat output showing exposure
tcp 0 0 0.0.0.0:18789 0.0.0.0:* LISTEN 12345/openclaw

That 0.0.0.0:18789 confirms it - my agent endpoint was accessible from the entire internet. No authentication barrier. No firewall rule blocking it. Just… open.

Why This Is Dangerous

OpenCLAW isn’t just a chatbot. It’s an agent with tool access:

  • Email - Read and send messages
  • Calendar - View and modify appointments
  • File systems - Access documents and data
  • Custom integrations - Whatever you’ve connected

A stranger messaging my agent could have asked it to read my emails, check my calendar, or worse. The Reddit poster was right - this is a critical security issue.

The Fix: Bind Gateway to Localhost

I needed to restrict OpenCLAW to only accept connections from localhost. Here’s how I did it.

Step 1: Check Current Configuration

First, I backed up my config:

backup-config.sh
cp ~/.config/openclaw/config.json ~/.config/openclaw/config.json.backup

Then I checked the full gateway configuration:

check-gateway-config.sh
openclaw config get gateway

Output:

Gateway config output
{
"host": "0.0.0.0",
"port": 18789
}

Confirmed. The host was set to 0.0.0.0, which means “listen on all interfaces.”

Step 2: Set Host to Localhost

I changed the host binding:

set-localhost-binding.sh
openclaw config set gateway.host 127.0.0.1

Step 3: Verify the Change

verify-config-change.sh
openclaw config get | grep host

Output:

Updated config output
host: 127.0.0.1

Good. But I needed to restart OpenCLAW for the change to take effect:

restart-openclaw.sh
sudo systemctl restart openclaw

Step 4: Confirm Localhost Binding

Let me verify the gateway is now bound correctly:

verify-localhost-binding.sh
sudo netstat -tulpn | grep 18789

Output:

Correct localhost binding
tcp 0 0 127.0.0.1:18789 0.0.0.0:* LISTEN 12345/openclaw

Now it shows 127.0.0.1:18789 - only accepting connections from localhost. Much better.

Here’s the difference:

Security comparison
BEFORE (INSECURE): AFTER (SECURE):
0.0.0.0:18789 127.0.0.1:18789
| |
v v
Internet ----> Agent Localhost only
Anyone can access Requires SSH tunnel

Access via SSH Tunnel

Now that OpenCLAW only listens on localhost, I can’t access it directly from my laptop. That’s the point. But I still need to use it.

The solution is SSH tunneling. This creates an encrypted tunnel from my local machine to the VPS, forwarding traffic to the OpenCLAW gateway.

Basic SSH Tunnel

From my laptop:

create-ssh-tunnel.sh
ssh -L 18789:localhost:18789 user@my-vps-ip

This command:

  • Creates a tunnel from local port 18789
  • To the VPS’s localhost:18789
  • All traffic is encrypted via SSH

Now I can access OpenCLAW at http://localhost:18789 on my laptop, which is actually tunneled to the VPS.

Background Tunnel (Persistent)

For a tunnel that runs in the background:

background-ssh-tunnel.sh
ssh -fN -L 18789:localhost:18789 user@my-vps-ip

The flags:

  • -f - Run in background
  • -N - Don’t execute remote command (just forwarding)
  • -L - Local port forwarding

SSH Config for Convenience

I added this to my ~/.ssh/config file:

~/.ssh/config
Host openclaw
HostName my-vps-ip
User my-username
LocalForward 18789 localhost:18789
ServerAliveInterval 60
ServerAliveCountMax 3

Now I just run:

connect-via-config.sh
ssh openclaw

And the tunnel is established automatically. The ServerAliveInterval keeps the connection alive and ServerAliveCountMax allows for brief network hiccups.

Test the Tunnel

Let me verify everything works:

test-tunnel-connection.sh
curl http://localhost:18789

Expected output (varies by OpenCLAW setup):

OpenCLAW response
{
"status": "ok",
"message": "OpenCLAW gateway running"
}

If I get “connection refused,” the tunnel isn’t active or OpenCLAW isn’t running.

Additional Security: Firewall

Even with localhost binding, I added a firewall rule as an extra layer of defense:

firewall-configuration.sh
# Allow SSH (critical - don't lock yourself out!)
sudo ufw allow ssh
# Explicitly deny external access to OpenCLAW port
sudo ufw deny 18789
# Enable firewall
sudo ufw enable
# Check status
sudo ufw status

Output:

UFW status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
18789 DENY Anywhere

This ensures that even if I accidentally change the gateway host back to 0.0.0.0, the firewall will block external access.

Quick Checklist

If you’re running OpenCLAW on a VPS, run through this checklist:

  1. Check exposure: openclaw config get | grep host
  2. Fix if needed: openclaw config set gateway.host 127.0.0.1
  3. Restart: sudo systemctl restart openclaw
  4. Verify binding: sudo netstat -tulpn | grep 18789
  5. Set up tunnel: Add SSH config or use -L flag
  6. Test access: curl http://localhost:18789

Why This Matters

The OpenCLAW agent is powerful. It’s designed to have access to your digital life - email, calendar, files, and more. That power becomes a liability if unauthorized users can interact with it.

Network-level security (localhost binding + SSH tunneling) provides defense-in-depth:

  • No authentication bypass - Even if the agent has no auth, the network blocks access
  • Encryption in transit - SSH tunnel encrypts all traffic
  • Single entry point - Only SSH port needs to be exposed
  • Audit trail - SSH logs show who connected and when

Don’t rely on “nobody knows my IP” as a security measure. Port scanners find exposed services in minutes.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments