Running OpenClaw on your laptop is safe by default, because your laptop is not open to the internet. Running OpenClaw on a rented server is different. Within minutes of renting the server, scanners such as Shodan and Censys find its address. Bots start trying passwords against SSH. Any service you leave open becomes a way in to OpenClaw's memory, credentials, and compute.
This guide sets up a server that keeps OpenClaw safe: locked down with keys instead of passwords, closed to public scans, sandboxed at the operating system level, and reachable only through a private network. It uses Hetzner Cloud for the server, Tailscale for the private network, and systemd to isolate the OpenClaw process.
What You Are Defending Against
Before you run any commands, know what you are protecting the server from.
- Scanners. Bots constantly scan the internet for open ports (22, 80, 443) and for services with known weaknesses. Any service you expose gets found within hours.
- Password attacks. Botnets run automated password guessing against SSH and web logins.
- A compromised OpenClaw process. If OpenClaw is tricked into running attacker code (for example, through a prompt injection), and OpenClaw is running as root, the attacker gets full control of the server.
- Stolen data. Anyone who reaches OpenClaw's files can read its memory (chat history, summaries) and its credentials (API keys, tokens).
The plan has four parts: keep the network closed to the public (no open management ports), run OpenClaw with the fewest privileges it needs, use cryptographic keys instead of passwords, and patch the server automatically.
1. Create an SSH Key
Use Ed25519 keys, not RSA. An Ed25519 key is 256 bits and is already as strong as a 4096-bit RSA key. It also verifies faster and resists more side-channel attacks.
Generate the key pair on your own computer, not on the server:
ssh-keygen -t ed25519 -C "openclaw-admin-2026" -f ~/.ssh/hetzner_ed25519-t ed25519picks the Ed25519 algorithm.-Cadds a label. Use the label later to track which key is which, across servers and over time.-fsets a specific file path, so you do not overwrite your existing default key.
Set a passphrase when asked. If your laptop is ever stolen, the passphrase is the only thing that stops someone from using the key immediately. A key with no passphrase is as unsafe as a plain text password.
Read the public key back with:
cat ~/.ssh/hetzner_ed25519.pubTreat a static key as a liability. If you suspect a private key was exposed, remove its matching public key from every server's authorized_keys file and from your cloud provider's dashboard right away. Rotate keys about once a year, and use the -C label (for example, admin-2026, admin-2027) to track which key is current.
2. Set Up the Server
This guide uses Hetzner Cloud, but the same steps apply to any cloud server provider.
- Add your key first. In the Hetzner console, go to Security, then Add SSH Key, and paste in your
ed25519.pubkey. - Create the server:
- Image: Ubuntu 24.04 LTS (Noble Numbat).
- Type: CPX21 (3 vCPU, 4 GB RAM) or similar.
- SSH key: select the key you just uploaded. Do not skip this step. If you skip it, Hetzner emails you a root password instead, which is unsafe from the start.
3. Create Separate User Accounts
Connect as root. This is the only step in this guide where you use the root account directly.
ssh -i ~/.ssh/hetzner_ed25519 root@<PUBLIC_IP>Do not run OpenClaw as root. If OpenClaw's process is ever compromised while running as root, the attacker owns the whole machine. Instead, create two accounts:
ops: an admin account, with sudo access, for you to manage the server.agent: a locked-down account, with no login shell and no sudo, used only to run the OpenClaw process.
Create the ops account
adduser --gecos "" opsSet a strong password when asked. You need this password for sudo commands during setup. You will not use it to log in over SSH (SSH will use a key only), but it stops someone from getting admin rights if they somehow get shell access.
# Give ops sudo access for setup. We remove this again at the end.
usermod -aG sudo ops
# Copy your SSH key to the ops account
mkdir -p /home/ops/.ssh
cp /root/.ssh/authorized_keys /home/ops/.ssh/
chown -R ops:ops /home/ops/.ssh
chmod 700 /home/ops/.ssh
chmod 600 /home/ops/.ssh/authorized_keysCreate the agent account
adduser --system --group --home /home/agent --shell /usr/sbin/nologin agentThis account has no login shell and no password. Its only job is to own the OpenClaw process.
Check that ops can log in
Open a new terminal on your own computer:
ssh -i ~/.ssh/hetzner_ed25519 ops@<PUBLIC_IP>If that works, close the root session. From here on, run every command as ops.
4. Turn Off Password Login
Ubuntu 24.04 has a setup quirk that can leave password login on by accident, even after you turn it off.
The hidden override
The usual fix is to edit /etc/ssh/sshd_config and set PasswordAuthentication no. On Ubuntu 24.04, a separate file at /etc/ssh/sshd_config.d/50-cloud-init.conf sets PasswordAuthentication yes on first boot. This file loads after the main config, so it wins. You can turn password login off in the main file and still have it on, without knowing.
Fix it
Remove the file that overrides your setting:
sudo rm -f /etc/ssh/sshd_config.d/50-cloud-init.confThen add your own settings file, so a future update cannot bring password login back without you noticing:
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf > /dev/null << 'EOF'
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
PubkeyAuthentication yes
X11Forwarding no
EOFPermitRootLogin nomeans every login goes through theopsaccount. Even if your key is exposed, the attacker lands asops, not root.UsePAM nostops other login methods from turning password login back on.X11Forwarding noremoves a feature this server does not need, since it has no display.
Reload SSH to apply the change:
sudo systemctl reload sshCheck that it worked
From your own computer, try to log in with a password instead of a key:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no ops@<PUBLIC_IP>This must fail with Permission denied (publickey). If it asks for a password instead, the override file is still active.
5. Turn On Automatic Security Updates
A server you set up and forget builds up unpatched security holes over time. Set up unattended-upgrades so security patches install on their own.
sudo apt update && sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgradesChoose Yes when asked.
Check that the security update source is turned on, in /etc/apt/apt.conf.d/50unattended-upgrades:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";Turning on automatic reboot is a real tradeoff, but it is needed. Kernel patches do not apply until the server reboots. A 3:30 AM reboot keeps the disruption small. If you run work that cannot handle a reboot at any time, turn this off and reboot by hand on your own schedule instead.
6. Close the Server to the Public Internet
So far, the server still listens for SSH on the public internet. This step closes that off, so only your own devices can reach it.
6.1 Install Tailscale
Tailscale builds an encrypted private network (a WireGuard mesh) between your devices. Instead of leaving SSH open to everyone, you move it onto this private network.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale upPiping a remote script into sh means you trust that script sight unseen. This guide does it here, and again for Homebrew in section 8, for the sake of brevity, not because it is the safest option. For more assurance, download the script first, read it, then run it:
curl -fsSL https://tailscale.com/install.sh -o install.sh && less install.sh && sh install.shFollow the URL Tailscale gives you to sign in. Your server now belongs to your private network (your "tailnet").
Save the server's Tailscale address. You will need it to set up OpenClaw:
tailscale ip -4
# example: 100.105.45.126.2 Set up the firewall
Use UFW to block everything by default, then allow only what you need.
Set the defaults:
sudo ufw default deny incoming
sudo ufw default allow outgoingAllow SSH, but only through the Tailscale network:
sudo ufw allow in on tailscale0 to any port 22 proto tcpAny SSH attempt against the server's public address now gets dropped without a response.
Allow Tailscale's own connection port (UDP 41641). Tailscale uses this port so devices can connect to each other directly instead of relaying traffic, which keeps the connection fast. Traffic here is still end to end encrypted. It does not expose any application, since Tailscale drops any packet that does not pass its own encryption check. From the outside, the port still looks closed.
sudo ufw allow 41641/udpTurn the firewall on carefully, in this order:
- Make sure Tailscale is running on your own computer.
- Open a second terminal window.
- In that second terminal, connect over Tailscale:
ssh -i ~/.ssh/hetzner_ed25519 ops@100.105.45.12. - If that connects, go back to your first terminal and turn the firewall on:
sudo ufw enable. - Confirm the public address no longer works:
ssh ops@<PUBLIC_IP>should time out.
Do not turn the firewall on before you confirm step 3 works. If Tailscale is not connected yet, turning on a deny-everything firewall locks you out, and the only way back in is Hetzner's web console.
6.3 Add Fail2Ban as a backup
With SSH only reachable over Tailscale, Fail2Ban should have little to do, which is the point. Keep it running anyway, as a second layer of defense in case a future change reopens a port by mistake.
sudo apt install fail2ban -yCreate /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = ssh
filter = sshdSet backend = systemd. Without it, Fail2Ban reads the older /var/log/auth.log file, which may not exist or update correctly on current Ubuntu systems, so it can silently miss attacks.
sudo systemctl enable fail2ban --now7. Bind OpenClaw to the Private Network
The single most important setting for OpenClaw is which address it listens on.
0.0.0.0: opens OpenClaw's control panel to the whole internet. Never use this.127.0.0.1: local only. Safe, but you cannot reach it from your laptop, even over Tailscale, unless you also set up SSH port forwarding.- Your Tailscale address (for example,
100.105.45.12): reachable only from your own devices on the private network. This is the setting to use.
Get your server's Tailscale address:
tailscale ip -4Edit the OpenClaw config file, usually at ~/.openclaw/config.json:
{
"gateway": {
"bind": "100.105.45.12",
"port": 18789
}
}Allow this port only over Tailscale:
sudo ufw allow in on tailscale0 to any port 18789 proto tcp8. Install Node.js
Ubuntu's own package manager keeps older, more stable software versions. OpenClaw's tooling generally needs a current Node.js release. Homebrew installs the latest version in your own home folder, without needing root access or touching system files.
This is one valid choice among several. Tools like nvm, a Node.js download, or Docker work just as well. This guide uses Homebrew because it can manage Node.js and other small tools in one place.
# Run this as ops, not as root
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to your shell
(echo; echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"') >> ~/.bashrc
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
# Install Node.js
brew install node
corepack enable
corepack prepare pnpm@latest --activate9. Install and Check OpenClaw
9.1 Install
Install through npm. This checks package signatures automatically, which is safer than piping an unknown install script for application code.
npm install -g @openclaw/cliThen run the setup wizard with the daemon flag, so it sets up the systemd service for you:
openclaw onboard --install-daemonThis command:
- creates the
~/.openclawfolder with restricted permissions (700) - writes a starting
config.jsonbound to127.0.0.1(you will change this to your Tailscale address, as shown above) - installs a sandboxed systemd service file at
/etc/systemd/system/openclaw.service - sets the service to run as the
agentaccount, withNoNewPrivilegesand filesystem isolation turned on - turns on the service so it starts on boot
9.2 Run a security check
Before you rely on this setup, run OpenClaw's built-in check:
openclaw security audit --deepThis looks at whether the gateway is bound to 0.0.0.0, whether credential files have safe permissions (600), and whether installed plugins are intact. If it finds problems, fix them automatically with:
openclaw security audit --deep --fix10. Remove Admin Rights When Done
Setup is finished. The ops account no longer needs sudo access day to day. Removing it limits the damage if the ops account is ever compromised.
# Run this as ops
sudo deluser ops sudoAfter this, ops cannot run admin commands. If you need admin access again later, use Hetzner's web console to log in as root, add sudo back to ops temporarily, do the task, then remove it again.
If you would rather keep sudo on ops permanently, that is a reasonable choice for a single person managing their own server. The NoNewPrivileges=yes setting in the systemd service still stops the OpenClaw process itself from ever using sudo, even if it is compromised.
11. Limit What OpenClaw Can Access
11.1 Treat OpenClaw like an outside contractor
Give OpenClaw the same access you would give a contractor you do not fully trust yet: only what the current task needs.
Scope every credential:
- GitHub: do not use your personal access token. Create a fine-grained personal access token instead. Choose only the repositories OpenClaw needs. Grant
Contents: Read/Writeonly, and remove Metadata, Admin, and every other scope. - OpenRouter: set a hard monthly spending limit, for example $10 to $20, in the OpenRouter dashboard. This limit protects you if OpenClaw gets stuck in a loop or is misused.
- General rule: give every key OpenClaw holds the smallest scope it needs. A leaked key with admin rights to a repository is a serious problem. A leaked key that can only read one repository is a minor one.
11.2 Lock down the Telegram bot
When you create your bot with @BotFather:
- Turn off group access. Send
/setjoingroupsand choose Disable. This stops anyone from adding your OpenClaw bot to a public group, where it could leak data or respond to strangers. - Restrict direct messages. In the OpenClaw config, set
dmPolicytopairingorallowlist. Never set it toopen. - Allow only your own account. Add your Telegram numeric ID as the only allowed user.
11.3 Choose how you talk to OpenClaw
- Prefer the terminal interface and Telegram over the web interface. The web interface exposes more surface area to attack.
- If you do use the web interface, reach it only through the Tailscale address (
http://100.105.45.12:18789), never over the public internet. - Do not ask OpenClaw to browse websites you do not trust. Prompt injection through web content is a real, demonstrated attack.
12. Watch for Failures
A server you cannot see into is a server you cannot trust. At the least, set up a check that alerts you if OpenClaw stops running.
Create /usr/local/bin/healthcheck-agent.sh:
#!/bin/bash
set -euo pipefail
if systemctl is-active --quiet openclaw; then
# OpenClaw is running, so ping the monitoring service
curl -fsS --max-time 10 -o /dev/null https://hc-ping.io/YOUR-UUID-HERE
else
# OpenClaw is down, so log it (the missed ping triggers an alert)
logger -p user.err "openclaw service is not active"
fiSign up for a free service such as Healthchecks.io, which alerts you by email, Slack, or Telegram if the expected ping does not arrive.
Schedule the check with cron:
crontab -e
# add this line:
*/5 * * * * /usr/local/bin/healthcheck-agent.shTo watch OpenClaw's logs directly:
journalctl -u openclaw -f13. Back Up OpenClaw's Data
OpenClaw's memory, settings, and credential files cannot be recovered if you lose them and have no backup.
Create /usr/local/bin/backup-agent.sh:
#!/bin/bash
set -euo pipefail
DATE=$(date +%Y-%m-%d)
SRC="/home/agent/.openclaw/"
DEST="user@backup-host:/backups/openclaw/"
LOG_TAG="openclaw-backup"
if rsync -avz --delete \
-e "ssh -i /home/ops/.ssh/backup_key" \
"$SRC" "$DEST"; then
logger -p user.info -t "$LOG_TAG" "Backup completed successfully for $DATE"
else
logger -p user.err -t "$LOG_TAG" "Backup FAILED for $DATE"
# optionally ping a failure webhook here
exit 1
fiset -euo pipefailstops the script on the first error, instead of hiding it.--deletekeeps the backup an exact copy of the source.-zcompresses the data while it copies.
The backup destination can be a second server, a NAS, or cloud storage mounted as a drive.
Schedule it daily:
crontab -e
# add this line:
0 4 * * * /usr/local/bin/backup-agent.sh14. Final Checklist
Before you trust this server, check every layer:
| Layer | Check | Expected result |
|---|---|---|
| SSH | ssh -o PreferredAuthentications=password ops@<PUBLIC_IP> |
Permission denied (publickey) |
| SSH | ssh ops@<PUBLIC_IP> |
Times out (port closed) |
| SSH | ssh ops@<TAILSCALE_IP> |
Connects |
| SSH | ssh root@<TAILSCALE_IP> |
Permission denied |
| Firewall | sudo ufw status |
Deny by default, only tailscale0 rules and 41641/udp allowed |
| Patches | systemctl status unattended-upgrades |
Active |
| OpenClaw | systemctl status openclaw |
Active, running as the agent account |
| Sandbox | cat /proc/$(pgrep -f openclaw)/status | grep NoNewPrivs |
NoNewPrivs: 1 |
| Audit | openclaw security audit --deep |
No high severity warnings |
References
- Ed25519: Bernstein, D. J., et al. High-speed high-security signatures. Journal of Cryptographic Engineering (2012).
- Ubuntu 24.04 SSH override: Launchpad Bug #2088207.
- Tailscale NAT traversal: How NAT Traversal Works.
- Systemd hardening: Mastering systemd Security. Red Hat.
- GitHub fine-grained tokens: Introducing Fine-Grained PATs.
- Unattended upgrades: Ubuntu Automatic Security Updates.