← Back to Blog

OpenClaw on a VPS: Self-Host Your AI Assistant Safely (Tested Setup)

Published · by RS Computers

OpenClaw AI agents Security

To run OpenClaw on a VPS safely, install it as a normal Linux user without sudo rights, keep its gateway on 127.0.0.1 port 18789 behind a long token, reach the dashboard through an SSH tunnel or Tailscale, and change the tool settings before you connect a chat app. That last step matters most. In our lab, the onboarding wizard of OpenClaw 2026.10.2 left the agent on the "full" tool profile, and when the model asked to run a shell command, it ran directly on the server without asking anyone. Plan on at least 4 GB of RAM: the gateway alone used about 1 GB idle and 1.4 to 1.5 GB while busy.

Key facts, checked on 12 October 2026:

What OpenClaw does, in plain words

OpenClaw is a personal AI assistant you host yourself. One long-running program, the gateway, connects your chat apps (WhatsApp, Telegram, Discord, Slack, Signal, iMessage, Microsoft Teams and about 20 more) to an AI model you pick. Our install offered almost 100 sign-in options, from Anthropic, OpenAI and Google to OpenRouter and local models through Ollama or LM Studio. The gateway runs on your server and keeps memory, chat history and credentials there.

What makes it more than a chatbot is its tools: the model can read and write files, run shell commands, drive a browser and schedule jobs. Break into a normal web app and data leaks; break into an agent with a shell and commands run. Every setting below shrinks what a stranger, a bad link or a confused model can make it do.

What the defaults left open in our lab

We installed 2026.10.2 twice in Debian 13 containers, with the official rootless installer and with the official Docker image. The native gateway listened only on 127.0.0.1 and ::1, required a token, and kept ~/.openclaw at mode 700. Four other defaults need your attention:

Binding to 127.0.0.1 is necessary but not enough. Advisory GHSA-g8p2-7wf7-98mq (31 January 2026, CVSS 8.8) let a crafted link steal the gateway token through the owner's own browser, even on a loopback-only gateway. CVE-2026-25593 (4 February 2026, CVSS 8.4) let any local process without a token rewrite the config and run commands. Current releases fix both, but only servers that update get the fix, so updates are part of the security setup.

The security checklist

The whole guide on one screen. The sections below show how we tested each row.

CheckWhy it mattersHow we verified it
Gateway on 127.0.0.1 onlyThe dashboard controls everything the agent can doss -ltn showed only 127.0.0.1 and [::1]; outside connections timed out
Token of 64 hex charactersLocal programs are attackers too (CVE-2026-25593)Kept in ~/.openclaw/.env, mode 600
Firewall: deny incoming, allow SSHCatches services you start later and forgetufw status verbose
Docker ports published on 127.0.0.1Published container ports bypass UFWdocker compose config showed host_ip: 127.0.0.1
Own user, no sudoA hijacked agent gets that user's rights and nothing moreid claw lists no sudo group
Tool profile other than "full"The default lets the model run shell commands without approvalTools offered to the model fell from 12 to 4
Shell commands only in a sandboxA throwaway container with no network limits the damageThe test command printed uid=1000(sandbox) and NO-NETWORK
Elevated exec offIt is the way out of the sandboxAudit: tools.elevated: disabled
API keys as references, not plain textConfig files end up in backups, tickets and screenshotsgrep found 0 copies of the key in openclaw.json
DM pairing and mention-only groupsStrangers can't talk to your agentFrom the docs; needs a real chat account, so not lab-tested
Weekly updates722 advisories in just over seven monthsopenclaw update status; a real update from 2026.10.1
Verified, encrypted backupsArchives contain the token, keys and chat historyopenclaw backup create --verify and a test restore
Audit after every changeSettings drift as you add channels and skillsopenclaw security audit: 0 critical findings
One gateway per group of people who trust each otherAnyone who can message the agent can steer its toolsOpenClaw's trust model

Layer 1: a user without sudo, and a closed firewall

On a fresh Debian 13 server, logged in as root, create a user called claw that owns OpenClaw and nothing else. "Linger" lets that user's background services start at boot without anyone logged in. The two install lines copy root's SSH key to claw, so they assume you log in with a key, not a password. For SSH hardening beyond this, see our Debian 13 security baseline.

# as root
apt update
apt install -y curl ca-certificates git ufw dbus-user-session
useradd -m -s /bin/bash claw
loginctl enable-linger claw
install -d -m 700 -o claw -g claw /home/claw/.ssh
install -m 600 -o claw -g claw /root/.ssh/authorized_keys /home/claw/.ssh/authorized_keys
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw --force enable
ufw status verbose

The last command should show Status: active and 22/tcp ALLOW IN for IPv4 and IPv6. Port 18789 is not opened, and it never will be.

Layer 2: install OpenClaw as that user

OpenClaw ships two Linux installers. install.sh installs Node system-wide from NodeSource's packages, which takes root. install-cli.sh downloads its own Node into ~/.openclaw and needs no root at all (installer docs). We used the second. Connect with ssh claw@YOUR_SERVER_IP:

# as the claw user
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install-cli.sh | bash
echo 'export PATH="$HOME/.openclaw/bin:$PATH"' >> ~/.profile
exit

It printed OpenClaw installed (OpenClaw 2026.10.2 (b8952d4)) after 58 seconds and used 820 MB of disk, including its own Node 24.21.0. Log in again so the new PATH applies. Now create the gateway token in a file only claw can read, and run the setup wizard without questions:

# as the claw user, after logging in again
openclaw --version
umask 077
printf 'OPENCLAW_GATEWAY_TOKEN=%s\n' "$(openssl rand -hex 32)" > ~/.openclaw/.env
set -a; . ~/.openclaw/.env; set +a
openclaw onboard --non-interactive --accept-risk --mode local \
  --auth-choice skip --gateway-bind loopback --gateway-auth token \
  --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
  --skip-channels --skip-search --skip-skills --install-daemon

The config now refers to the token by its variable name instead of containing it, and --install-daemon creates a systemd user service that brings the gateway back if it crashes (we saw that happen in our memory test). On the first start the wizard may print "Gateway did not become reachable". Ours was still loading: the first start took about a minute, and the gateway answered 6 to 12 seconds after that message. Later restarts took 21 to 26 seconds. Check it:

# as the claw user
curl -fsS http://127.0.0.1:18789/healthz
ss -ltn | grep 18789
openclaw config validate
openclaw security audit

Expected: {"ok":true,"status":"live"}, two LISTEN lines on 127.0.0.1 and [::1] only, Config valid, and an audit with 0 critical findings. Its one warning, gateway.trusted_proxies_missing, only matters if you add a reverse proxy.

We skipped the model step because a lab can't sign in to a provider. For that step, OpenClaw's own doctor check points to openclaw configure --section model, which we did not test. To keep the API key out of the config, put it in ~/.openclaw/.env and reference it with openclaw config set <path> --ref-provider default --ref-source env --ref-id <VARIABLE>. After we did that for a test key, grep found no copy of it in openclaw.json. Never pass keys as command-line flags: they land in your shell history.

Layer 3: reach the dashboard through a tunnel, not a port

An SSH tunnel carries the dashboard over your SSH login. Run this on your own computer and leave it open:

# on your own computer
ssh -N -L 18789:127.0.0.1:18789 claw@YOUR_SERVER_IP

On the server, openclaw dashboard --json prints a browserUrl with a one-time pairing code that works for ten minutes. Open it in your browser while the tunnel runs. Through the same kind of tunnel in our lab, /healthz answered and the page title read "OpenClaw Control", while a direct connection to port 18789 from outside timed out.

From a phone, Tailscale is more comfortable. According to the Tailscale docs, gateway.tailscale.mode: "serve" keeps the gateway on 127.0.0.1 and serves it over HTTPS to your own tailnet devices only, while funnel publishes it to the internet. Our Tailscale VPS guide covers the install. If you need a public URL, use a reverse proxy with TLS, set gateway.trustedProxies and switch to password or trusted-proxy authentication, as the network exposure docs describe. Both paths need an account or a domain, so we did not lab-test them.

Layer 4: decide what the model's hands can do

To see what the model can do, we pointed OpenClaw at a small local stand-in that speaks the OpenAI API and always asks to run one command: id; hostname, a network check and a look for the Docker socket. With the onboarding defaults, it ran on the server as claw, reached the internet, and nobody was asked. Pick one of two fixes.

Option A: a chat assistant with no shell

This is OpenClaw's own hardened baseline, plus switching off local network discovery (mDNS):

# as the claw user
cat > ~/hardening.json5 <<'EOF'
{
  session: { dmScope: "per-channel-peer" },
  tools: {
    profile: "messaging",
    deny: ["group:automation", "group:runtime", "group:fs", "sessions_spawn", "sessions_send"],
    fs: { workspaceOnly: true },
    exec: { security: "deny", ask: "always" },
    elevated: { enabled: false },
    sessions: { visibility: "agent" },
    agentToAgent: { enabled: false },
  },
  discovery: { mdns: { mode: "off" } },
}
EOF
openclaw config patch --file ~/hardening.json5
openclaw config validate

It applied without a restart. The tools offered to the model fell from 12 to 4 (sessions_yield, tool_call, tool_describe, tool_search), and the audit switched to tools.elevated: disabled.

Option B: shell commands, but only inside a container

If the agent should run code, let it do so in a Docker sandbox. The trade-off: membership of the docker group equals root on that server, so a compromised gateway could misuse it. OpenClaw also supports rootless Podman (backend: "podman"); we tested Docker only.

# as root
apt install -y docker.io
usermod -aG docker claw
systemctl restart user@$(id -u claw).service

Restarting claw's user services restarts the gateway too, so it picks up the new group (we checked in /proc). Then build the sandbox image from the sandboxing docs and turn the sandbox on:

# as the claw user
docker build -q -t openclaw-sandbox:bookworm-slim - <<'DOCKERFILE'
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  bash ca-certificates curl git jq python3 ripgrep \
  && rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
WORKDIR /home/sandbox
CMD ["sleep", "infinity"]
DOCKERFILE
cat > ~/sandbox.json5 <<'EOF'
{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        backend: "docker",
        scope: "session",
        workspaceAccess: "rw",
        docker: { network: "none", readOnlyRoot: true, capDrop: ["ALL"] },
      },
    },
  },
  tools: {
    profile: "coding",
    deny: ["group:automation", "sessions_spawn", "sessions_send"],
    fs: null,
    exec: { host: "sandbox", security: null, ask: null },
    elevated: { enabled: false },
    sessions: { visibility: "agent" },
    agentToAgent: { enabled: false },
  },
}
EOF
openclaw config patch --file ~/sandbox.json5
openclaw sandbox explain

The null values remove Option A's file and exec limits, in case you applied it first. The image built in 20 seconds (218 MB), and sandbox explain printed runtime: sandboxed. The same test command now printed uid=1000(sandbox), a container ID as hostname, Debian 12, NO-NETWORK and no Docker socket. An idle sandbox container used 652 KB of RAM; Docker itself added about 170 MB.

Keep the chat side strict too. According to OpenClaw's README, direct-message channels pair unknown senders by default, and you approve each one with openclaw pairing approve <channel> <code>. We could not test this without a chat account. Groups should require a mention, and WhatsApp or Signal deserve a separate phone number. If the agent needs your own data, a read-only MCP server is a narrower door than a shell.

Running it in Docker instead: publish on 127.0.0.1

The official image (ghcr.io/openclaw/openclaw, 3.3 GB) runs as the unprivileged node user with no-new-privileges and an unless-stopped restart policy. The catch is the port list. An override file fixes it, and Compose reads docker-compose.override.yml automatically:

# as root
apt install -y docker.io docker-compose git openssl
git clone -q --depth 1 --branch v2026.10.2 https://github.com/openclaw/openclaw.git /opt/openclaw
cd /opt/openclaw
install -d -o 1000 -g 1000 -m 700 /opt/openclaw-data /opt/openclaw-data/config /opt/openclaw-data/workspace /opt/openclaw-data/secrets
umask 077
cat > .env <<ENV
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:2026.10.2
OPENCLAW_CONFIG_DIR=/opt/openclaw-data/config
OPENCLAW_WORKSPACE_DIR=/opt/openclaw-data/workspace
OPENCLAW_AUTH_PROFILE_SECRET_DIR=/opt/openclaw-data/secrets
OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)
ENV
cat > docker-compose.override.yml <<'YAML'
services:
  openclaw-gateway:
    ports: !override
      - "127.0.0.1:18789:18789"
YAML
docker compose pull -q openclaw-gateway
docker compose run -T --rm --no-deps --entrypoint node openclaw-gateway \
  dist/index.js onboard --non-interactive --accept-risk --skip-health \
  --mode local --auth-choice skip \
  --gateway-auth token --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
  --skip-channels --no-install-daemon
docker compose run -T --rm --no-deps --entrypoint node openclaw-gateway \
  dist/index.js config set --batch-json '[{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]'
docker compose up -d openclaw-gateway
docker compose config | grep -A6 '^    ports:'

The last command shows host_ip: 127.0.0.1 and only port 18789. From outside, our connection to 18789 was refused although this container had no firewall. The Docker install also started on the "full" tool profile. Save the Option A file as /root/hardening.json5 and, still in /opt/openclaw, apply it with docker compose exec -T openclaw-gateway node dist/index.js config patch --stdin < ~/hardening.json5. A sandbox inside Docker means mounting the Docker socket into the gateway, which hands it the host; for Option B, use the native install. Compose basics are in our Docker on a VPS guide.

Updates and backups

Back up first, then update. OpenClaw's backup command copies its SQLite databases safely while the gateway runs. Never copy the live .sqlite files by hand (backup docs).

# as the claw user
mkdir -p ~/backups && chmod 700 ~/backups
openclaw backup create --output ~/backups --verify
openclaw update status
openclaw update --dry-run
openclaw update --yes

Our first archive took 4.7 seconds and was 190 KB. It skips the reinstallable 820 MB Node folder but includes .env, so treat every archive as a secret. A real update from 2026.10.1 to 2026.10.2 took 11 minutes 20 seconds on two busy vCPUs, with 202 seconds of gateway downtime. The updater also left the previous runtime (about 600 MB) on disk until the next update or openclaw doctor --fix.

For a nightly backup that keeps 14 days, install cron as root (apt install -y cron) and add the job as claw:

# as the claw user
( crontab -l 2>/dev/null; echo '30 3 * * * $HOME/.openclaw/bin/openclaw backup create --output $HOME/backups --verify >> $HOME/backups/backup.log 2>&1; find $HOME/backups -name "*-openclaw-backup.tar.gz" -mtime +14 -delete' ) | crontab -
crontab -l

A backup on the same disk is not a backup. Copy ~/backups off the server with encryption; our restic offsite backup guide shows how. To test a restore, openclaw backup restore ARCHIVE --target ~/restore-test verifies the archive and unpacks it into an empty folder. Ours worked, and it warned that WhatsApp may need relinking after a rollback.

How big a server: our measurements

Measurement (OpenClaw 2026.10.2, Debian 13, 2 vCPU)Result
Gateway RAM, idleAbout 1 GB (988 to 1,053 MB for the process and its workers)
Gateway RAM, six chats at once1,378 MB peak; eight at once peaked at 1.49 GB
Docker image instead, idle1.14 to 1.19 GiB (docker stats)
Twelve chats at once, 4 GB limitOut of memory: the kernel killed the gateway and systemd restarted it 5 seconds later
2 GB limit1.2 GB used idle, plain chats worked, the first sandboxed command ran out of memory
Disk820 MB program, 40 to 80 MB state, 218 MB sandbox image, 600 MB per retained update

The twelve-chat test was harsh on purpose (each test client is its own Node process), but browsers, sandboxes and extra channels add up too. Our advice for RS Computers plans:

All plans run on KVM with NVMe, unmetered traffic and their own IPv4 and IPv6, and you can upgrade later from the client area with a short reboot. Servers are in Amsterdam, Dublin and Prishtina; availability by city is on the plans page. Comparing agents? See Hermes Agent on Telegram, Claude Code and Codex running 24/7 and a Claude Code trading bot on a VDS. Our bot hosting guide shows how to get a Telegram bot token from BotFather.

Frequently asked questions

Is OpenClaw safe to run on a VPS?

It can be, if you change the defaults. Keep the gateway on 127.0.0.1 with token authentication, run it as a user without sudo, replace the "full" tool profile with a restricted one or a Docker sandbox, and update often: the project published 722 security advisories between January and September 2026.

How much RAM does OpenClaw need?

Plan on 4 GB. In our lab, OpenClaw 2026.10.2 used about 1 GB idle and up to 1.4 GB with six parallel chats; with a 2 GB limit, the first sandboxed command ran out of memory. Use 8 GB for sandboxes, browser tools or several channels.

What port does OpenClaw use, and should I open it?

The gateway and its web dashboard use port 18789. Don't open it in the firewall. Reach it through an SSH tunnel with ssh -N -L 18789:127.0.0.1:18789 user@server, through Tailscale Serve, or behind a reverse proxy with authentication.

Can I run OpenClaw without Docker?

Yes. The rootless install-cli.sh installer puts Node and OpenClaw under ~/.openclaw and runs the gateway as a systemd user service. You only need Docker for the optional sandbox that runs the agent's shell commands.

Does OpenClaw need a GPU?

No. With a cloud provider such as Anthropic or OpenAI, the model runs at the provider and the server only runs the gateway. A GPU or much more RAM matters only for local models through Ollama or LM Studio.

How do I update OpenClaw safely?

Run openclaw backup create --output ~/backups --verify, then openclaw update --dry-run and openclaw update --yes as the user that owns the install. Expect downtime: in our lab, on two vCPUs of a busy test host, the update took 11 minutes 20 seconds and the gateway was down for 202 seconds.

Your assistant, on a server you control

OpenClaw is worth self-hosting when you set the limits: a user without sudo, a gateway nobody else can reach, a model that can't run commands outside a box, and backups you have restored once. Pick a VPS Mini or VDS Small on the plans page, or message us on Telegram if you want help choosing. If you would rather have us do the installation, we quote setup work per job on Telegram or by email.

← All articles

Chat on Telegram