All Articles OpenClaw

Deploying OpenClaw on a VPS: Hetzner, DigitalOcean, and Linode Compared

You want to run OpenClaw in the cloud. Not on your laptop. Not on a home server. Somewhere that's always on, always available, always secure.

You want to run OpenClaw in the cloud. Not on your laptop. Not on a home server. Somewhere that’s always on, always available, always secure. A Virtual Private Server (VPS) is the answer—but which one?

And here’s the problem: once you’ve rented the VPS, you’ve got a blank Ubuntu box, and the clock is ticking. Every minute it’s connected to the internet is a minute it could be compromised. So we need speed, precision, and a clear playbook.

But before we dive into deployment tactics, let’s talk about why this matters. A VPS is fundamentally different from running something on your laptop. On your laptop, you close it and it’s offline. On a VPS, the server is always facing the internet, always listening, always vulnerable. That’s why the hardening sequence matters. Skip the firewall and you’ll get port-scanned within hours. Skip fail2ban and you’ll see thousands of SSH brute-force attempts per day. Skip unattended upgrades and you’ll be running vulnerable software three months after the patch dropped.

The good news? With the right sequence and discipline, running OpenClaw on a VPS is actually safer than running it locally. You get isolation, automated backups, and professional infrastructure monitoring. You just need to get the setup right.

Let’s walk through provider selection, initial hardening, Docker setup, and OpenClaw deployment with Tailscale for secure access.

Provider Comparison: The Three Contenders

You’ve got hundreds of VPS providers. But at the price point where OpenClaw makes sense ($5-10/month), there are three that actually dominate: Hetzner, DigitalOcean, and Linode.

Why these three? Because they’ve solved the problem of cheap infrastructure without cutting corners on reliability. A $2/month VPS from some Eastern European datacenter might technically work, but when it goes down at 2 AM on a Sunday, there’s no support ticket you can file. You’re just waiting. DigitalOcean, Hetzner, and Linode have actual support, actual SLAs, and (more importantly) actual uptime. You’re paying a tiny premium for sleep-at-night assurance.

Hetzner Cloud

Pricing: $4.90/month (CX11, 2GB RAM, 1vCPU, 25GB SSD)

Strengths:

  • Genuinely cheap. Like, shockingly cheap for the specs. You’re getting more computing power per dollar than any other provider in this tier.
  • No bandwidth overage charges (unlimited outbound). This is the big one. DigitalOcean will bill you $0.20/GB after 1TB. Hetzner says “take what you need.” If you’re running a public-facing API or logs are flowing constantly, this difference adds up.
  • Multiple datacenters (US, EU, Asia). You’re not forced into one region if that doesn’t work for your users.
  • SSH key support from day one. No password authentication, which is a security win immediately.
  • Snapshot backups included. You can create point-in-time copies of your entire server without paying extra.
  • Fast storage (NVMe). Not spinning disks. This matters when OpenClaw is writing logs or caching data—NVMe is 10x faster.

Weaknesses:

  • Interface is less polished than DigitalOcean. It works fine, but it’s not as visually intuitive. You’ll figure it out, but there might be more clicking.
  • Smaller support community (though improving). If you hit a weird edge case, there are fewer Stack Overflow answers for Hetzner-specific issues.
  • Documentation is thinner. DigitalOcean has written a tutorial for literally everything. Hetzner’s docs are good but less comprehensive. You might need to extrapolate from general Linux docs.
  • API takes some learning. If you want to automate server creation, Hetzner’s API is powerful but has a steeper learning curve than some competitors.

Good for: Budget-conscious deployments (especially if you need to run multiple servers), EU customers (lower latency), high-volume deployments (unlimited bandwidth saves money at scale)

GPU Options: Not available in the cheapest tier. The CX21 ($5.90) still no GPU. You need to go CX31 ($6.90) for actual GPU options, and those aren’t cheap.

DigitalOcean

Pricing: $6.99/month (1GB RAM, 1vCPU, 25GB SSD)

Strengths:

  • Industry-leading documentation. DigitalOcean has written a tutorial for virtually every deployment scenario. Learning curve is steep? No—they’ve removed it with detailed guides. This cuts your setup time dramatically.
  • Clean, intuitive dashboard. Everything is where you expect it. First-time VPS users feel comfortable here. The UI respects your intelligence without overwhelming you with options.
  • App Platform (Heroku-like PaaS) if you want it. Don’t want to manage a VPS? DigitalOcean can handle deployment and scaling for you. It’s more expensive, but it’s an escape hatch if infrastructure gets tedious.
  • Kubernetes support. If you grow beyond a single VPS, DigitalOcean’s managed Kubernetes is solid. You’re not locked into a tiny provider.
  • Strong community presence. If you get stuck, there are thousands of people who’ve solved the same problem on DigitalOcean. Google any DigitalOcean question and you’ll find an answer within the first three results.
  • Security-first approach (private networks, VPCs, firewalls baked into the UI). Security isn’t an afterthought—it’s integrated into every decision.

Weaknesses:

  • More expensive than Hetzner. You’re paying $2+ per month more for the developer experience. For large-scale deployments, that compounds quickly.
  • Bandwidth starts counting against you ($0.20/GB after 1TB). If you exceed 1TB outbound monthly, every extra GB costs money. For API-heavy applications, this can sting.
  • Fewer datacenters than competitors. You get 6 regions instead of 12+. If you need to be close to a specific geography, you might be forced into a suboptimal choice.
  • SSH key setup slightly more clicks. It’s not a dealbreaker, but Hetzner’s key management is slightly faster.

Good for: Developers who prioritize documentation and ease of use (this is worth the extra $2/month if you’re new to DevOps), US-first deployments, teams who value a polished experience

GPU Options: Available via Droplets, but starts at $100+/month

Linode (Akamai)

Pricing: $6.99/month (2GB RAM, 1vCPU, 50GB SSD) or $5/month for Nanode tier (1GB RAM, 1vCPU)

Strengths:

  • Owned by Akamai (stable, funded). This is enterprise backing. Linode isn’t going anywhere. They have the resources to maintain infrastructure for decades.
  • Good value for specs (2GB RAM standard at the $6.99 tier). At the same price as DigitalOcean, you get twice the RAM. That’s real value if your application is memory-hungry.
  • Solid documentation. Not quite DigitalOcean-level, but comprehensive and clear. You’ll find what you need.
  • Private networking built-in. Linode assumes you want network isolation from day one. This is a security-first mindset.
  • Multiple regions (12+). Almost as many options as Hetzner. You can spread load geographically.
  • Nanode tier is dirt cheap ($5/month). If you’re running a lightweight hobby project, Nanode is the cheapest way to get a real server running.

Weaknesses:

  • Less trendy, smaller ecosystem. Fewer startups use Linode, so fewer blog posts and tutorials. If you’re looking for cutting-edge DevOps tooling, you’ll find it on DigitalOcean first.
  • Nanode tier is limited (1GB RAM, 1vCPU). The cheapest option is genuinely minimal. OpenClaw with a few plugins might push it.
  • Dashboard less intuitive than DigitalOcean. It’s not bad, just takes longer to learn. Everything you need is there, just buried one more menu level deep.
  • Support slower than competitors. Response times are measured in hours, not minutes. For non-emergencies, that’s fine. For production incidents, DigitalOcean is faster.

Good for: Budget deployments needing more RAM (the 2GB at $6.99 is a steal), teams that value long-term stability over trendiness, projects that benefit from Akamai’s edge network

GPU Options: Not mainstream, special request basis

The Verdict

For OpenClaw at $5-10/month scale, here’s the breakdown by use case:

  • Pure budget play: Hetzner CX11 ($4.90). If you’re bootstrapping and every dollar matters, Hetzner is unbeatable. You get 2GB RAM, unlimited bandwidth, and NVMe storage for less than a coffee subscription.

  • Best balance: Linode Nanode ($5) or Hetzner CX11. The Nanode is cheaper than you’d think, and with careful resource management, it works. But if you can swing another 50 cents a month, Hetzner’s extra RAM makes life easier.

  • Best experience: DigitalOcean Basic ($6.99) if budget allows. You’re paying for documentation, dashboard quality, and community knowledge. For a first-time DevOps user, this removes a lot of friction. That’s worth $2/month if you value learning speed.

  • Best specs: Linode Standard ($6.99, 2GB RAM). At the same price as DigitalOcean but with double the RAM. If memory is your bottleneck (and it often is with OpenClaw), this is the winner.

My recommendation? Start with Hetzner CX11. You’ll save $50-100/year compared to DigitalOcean, and the specs are identical. The DigitalOcean tax is paid in time saved reading docs, not in actual capability. If you have a small team or you’re experienced with Linux, Hetzner’s learning curve is flat. If you’re new to this, consider DigitalOcean’s $2/month “education tax” worth the investment.

Put your Hetzner savings toward your next server, monitoring, or backups. That’s where money actually creates value.

Deep Dive: Pricing Tiers and Total Cost of Ownership

Let’s be real about pricing. The advertised monthly cost is just the beginning. When you’re running OpenClaw in production, you need to account for backups, traffic overage, snapshots, and the inevitable scaling up when your initial choice proves too small.

Here’s the hidden cost most people miss: your server choice at launch will become inadequate in 6-12 months. Usage grows. You add features. You get more users. Or sometimes you just realize OpenClaw needs more memory than you expected. At that point, you’ll upgrade. The question isn’t “can I run on this cheap server forever,” it’s “what’s my cost for the first year, including the upgrade?”

Hetzner Total Cost for Year 1:

  • CX11 base: $4.90/month = $58.80/year
  • Automated backups: included
  • Snapshots: $0.0119/GB (you’ll want 2-3, say 50GB = $1.78/month)
  • Traffic: included (unlimited outbound, unlimited inbound)
  • Expansion to CX21 at month 6: $5.90/month × 7 months = $41.30
  • Total Year 1: ~$120 (conservative estimate)

DigitalOcean Total Cost for Year 1:

  • Droplet $6.99/month = $83.88/year
  • Backups at $1.44/month = $17.28/year
  • Traffic overage (you’ll hit this): assume 2TB/month excess = $0.20 × 1TB × 12 = $24/year
  • Snapshots (manual or through backups): included with backup
  • Expansion to 2GB at month 6: $12/month × 7 = $84
  • Total Year 1: ~$209

Linode Total Cost for Year 1:

  • Nanode $5/month = $60/year
  • Backups $2.50/month = $30/year
  • Traffic: included (outbound charged at $0.02/GB after 4TB, rare for OpenClaw)
  • Expansion to Linode 4GB at month 6: $18/month × 7 = $126
  • Total Year 1: ~$216

Hetzner’s pricing model is genuinely friendlier when you account for everything. You’re saving $80-100 in year one, which is real money if you’re bootstrapping. But here’s the hidden value in DigitalOcean: their documentation might be worth the extra $80/year if you’re learning DevOps for the first time. Every hour you save troubleshooting is an hour you spend building. The cheaper path isn’t always the faster path.

Also notice the pattern: both providers know you’ll upgrade around month 6. Hetzner and DigitalOcean both have cheap entry tiers designed to get you hooked. Once you’ve invested time in setting up, migrating is annoying, so you upgrade with them. That’s why the math works out so neatly.

Server Sizing: From Theory to Reality

OpenClaw’s resource needs depend on your workload type. This is where most people go wrong—they either over-provision (“better safe than sorry”) or under-provision (“we’ll upgrade later”). Both are expensive mistakes.

The hidden lesson: you can’t know your actual requirements until you run real workload for two weeks. The best strategy is to start small, monitor for a few weeks, then make an informed decision. But you need a framework for thinking about it. Let me break down different scenarios:

Scenario 1: Light Usage (Hobby/Personal Project)

  • Workload: 10-50 requests/day, small context windows
  • Requirements: 512MB RAM, 0.5 CPU
  • Provider Match: Linode Nanode (1GB), Hetzner CX11 (2GB) — both overkill, but minimum available
  • Estimated Cost: $5-6/month
  • Expected Latency: 100-200ms per query

Scenario 2: Small Team (5-10 people)

  • Workload: 100-500 requests/day, medium context windows, some concurrency
  • Requirements: 2GB RAM, 1-2 CPU cores
  • Provider Match: Linode Standard ($6.99, 2GB), Hetzner CX21 ($5.90, 4GB) — both good
  • Estimated Cost: $6-10/month
  • Expected Latency: 50-100ms per query

Scenario 3: Mid-Scale (30-100 people)

  • Workload: 1000-5000 requests/day, large context windows, peak concurrency
  • Requirements: 4-8GB RAM, 2-4 CPU cores
  • Provider Match: DigitalOcean $24/month (4GB), Linode 8GB ($24/month), Hetzner CX31 ($23/month)
  • Estimated Cost: $24-30/month
  • Expected Latency: 50-150ms per query (variable based on load)

Scenario 4: High-Volume (100+ people)

  • Workload: 5000+ requests/day, long contexts, heavy token usage
  • Requirements: 16GB+ RAM, 4-8 CPU cores, possibly GPU acceleration
  • Provider Match: Dedicated servers or managed Kubernetes
  • Estimated Cost: $100+/month
  • Expected Latency: Highly variable without load balancing

The key insight: don’t over-provision. Start with Scenario 1 or 2 (most teams are here), monitor your actual usage for 2-4 weeks, then upgrade if needed. It’s easier to scale up than to pay for unused capacity. Every dollar spent on unused memory is a dollar that could have gone to better backups, monitoring, or another feature.

Also notice: the biggest jump in cost is from Scenario 2 to 3 (goes from $10 to $24+). That’s because you’re crossing the line from “small enough that one person can manage it” to “large enough that you need automation and monitoring.” This is less about raw compute and more about operational overhead. A $24/month server requires more babysitting than a $6/month server. Budget for that complexity.

Benchmarking Methodology: Know Your Baseline

Before you deploy, you need to understand what “good performance” means for your setup. Most people skip this step and regret it six months later. You’ll get a support request saying “OpenClaw is slow,” and you won’t have any data to understand if it’s actually slow or if expectations are wrong.

Benchmarking is your insurance policy. Spend an hour now, save 10 hours later debugging phantom performance issues. Here’s how to benchmark:

Tool Selection:
Use ab (Apache Bench) for simple testing or vegeta for more sophisticated load testing:

# Install vegeta
go install github.com/tsenart/vegeta@latest

# Create a load test targeting your OpenClaw instance
echo "GET https://automateanddeploy.com:8000/api/health" | vegeta attack -duration=30s -rate=50 | vegeta report

Key Metrics to Capture:

  1. Latency (p50, p95, p99): How fast are typical requests? What about the slow ones?
  2. Throughput: Requests per second the server can handle
  3. Error Rate: What percentage fail under load?
  4. CPU Utilization: Are you maxing out cores? (Use top or docker stats)
  5. Memory Usage: Is the server swapping? (Watch free -h)

Baseline Benchmark Script:

#!/bin/bash
# benchmark-openclaw.sh

# Test 1: Single request latency
echo "=== Single Request Latency ==="
time curl -s https://automateanddeploy.com:8000/api/query -H "Content-Type: application/json" \
  -d '{"prompt":"Hello, world!"}' | jq '.'

# Test 2: Sustained load (50 req/sec for 60 seconds)
echo -e "\n=== 50 req/sec for 60 seconds ==="
echo "GET https://automateanddeploy.com:8000/api/health" | vegeta attack -duration=60s -rate=50 | vegeta report

# Test 3: Spike test (ramp up to 200 req/sec)
echo -e "\n=== Spike Test: 0->200 req/sec ==="
for i in {0..200..20}; do
  echo "GET https://automateanddeploy.com:8000/api/health" | vegeta attack -duration=10s -rate=$i | vegeta report | head -5
done

# Test 4: Concurrent connections
echo -e "\n=== Apache Bench (concurrency test) ==="
ab -n 1000 -c 50 https://automateanddeploy.com:8000/api/health

Run this before and after any infrastructure changes so you have data to compare against. The magic is in the numbers: if you claim an optimization made things faster, you need to show the before/after metrics. Gut feeling doesn’t count in production.

Store these baseline metrics somewhere (your team wiki, a shared document, wherever). Six months from now when someone says “is the server slower,” you can run the benchmark again and have objective data instead of arguing about it.

GPU Options and When to Use Them

Here’s the truth about GPUs for OpenClaw: you probably don’t need one for inference. You need a GPU if you’re fine-tuning models, running local LLMs with Ollama at scale, or doing heavy embedding operations.

The hidden lesson: GPU costs are non-linear. Going from $10/month to $20/month just doubles your compute. But going from $20/month to $120/month (adding a GPU) is a 6x jump. That’s because GPUs are expensive hardware. If you don’t absolutely need it, that $120/month becomes $1440/year. That’s enough money to hire someone to optimize your CPU-based code instead.

GPU-Enabled Providers:

  • Hetzner: GPU options start at CX-41 (~$16/month for NVIDIA T4)
  • DigitalOcean: GPU Droplets start at $100+/month (overkill for most uses)
  • Linode: GPUs available via API request, very limited availability
  • Lambda Labs: Specialized GPU provider, $0.44/hour for A100, good for testing

Should You Use a GPU?
Ask yourself these questions:

  • Am I running local LLMs with Ollama? (Yes → maybe GPU helps)
  • Am I doing real-time embeddings at scale? (Yes → GPU helps)
  • Am I calling Claude API? (No → GPU doesn’t help)
  • Do I need to quantize/compress models? (Yes → CPU fine, but GPU faster)

If you’re calling the Claude API, save your money. GPUs don’t speed up API calls. But if you’re running Ollama locally with a 7B parameter model and need sub-100ms latency on embeddings, a GPU becomes worth it.

Region Selection: Proximity and Performance

The “best” region is the one closest to your users (geography + network topology). Here’s the nuance: OpenClaw’s query latency is dominated by the API call to Anthropic’s servers (if using Claude API), not your local gateway. But local processing still matters. If your OpenClaw instance is processing a 10MB file before sending it to Claude, you want that processing fast. A 50ms delay locally turns into 5 seconds if you’re hitting Africa from Sydney.

Think of it like this: the Anthropic API call might be 500ms round-trip regardless of where your server is (it’s going to their US East servers anyway). But if your local processing is 100ms in Frankfurt and 1 second in Tokyo, that matters. Geography counts, just not as much as people think.

Hetzner Regions:

  • fsn1 (Falkenstein, Germany) — EU default, good latency to Europe
  • nbg1 (Nuremberg, Germany) — similar to fsn1
  • hel1 (Helsinki, Finland) — northernmost EU, good for Nordic customers
  • ash (Ashburn, Virginia, USA) — good for US-East
  • hil (Hillsboro, Oregon, USA) — best for US-West

DigitalOcean Regions:

  • nyc3 (New York) — US-East, tier-1 infrastructure
  • sfo3 (San Francisco) — US-West, dense tech ecosystem
  • lon1 (London) — EU, UK/EU hybrid
  • sgp1 (Singapore) — Asia-Pacific, best latency for Asia
  • blr1 (Bangalore, India) — India-specific, great for Indian users

Linode Regions:

  • 12+ regions globally
  • Most similar to DigitalOcean coverage
  • Excellent latency in all regions

Rule of Thumb: Pick the region closest to where your developers sit, not where your end users are. You’ll be SSH-ing into this server constantly. Latency matters there. A 500ms SSH round-trip makes a 10-minute debug session turn into 20 minutes. Over a year, that’s weeks of wasted time.

If you’re doing a multi-region setup (two OpenClaw instances), Hetzner + DigitalOcean in different regions is a solid strategy. Use Hetzner for EU, DigitalOcean for US. This also gives you redundancy—if one provider has an outage, you still have access. The extra cost is minimal compared to the peace of mind.

Initial Server Setup: The First Hour

You’ve got a fresh Ubuntu 22.04 LTS box. Here’s the hardening checklist that matters for OpenClaw deployment.

This section is critical because it’s the difference between a server that gets breached in 72 hours and a server that’s actually secure. Your new VPS is exposed to the internet immediately. Right now, automated bots are scanning every IP for SSH access, open ports, and known vulnerabilities. You have a small window to harden before they find you. Let’s use that window.

Step 1: SSH Key-Only Authentication

First, make sure you have your SSH key. Create one if needed:

ssh-keygen -t ed25519 -f ~/.ssh/openclaw -C "openclaw@yourserver"

When you provision the VPS, upload this public key immediately. Most providers let you do this during server creation.

Then, disable password authentication:

# Log in with your key first
ssh -i ~/.ssh/openclaw root@your-vps-ip

# Edit SSH config
sudo nano /etc/ssh/sshd_config

# Change/add these lines:
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no

# Restart SSH
sudo systemctl restart sshd

Test the new config before logging out:

# In a new terminal, test login
ssh -i ~/.ssh/openclaw root@your-vps-ip

Only log out of the original session once you confirm the new one works. This is a critical safety step. If you disable password auth first and then lock yourself out of SSH, you’re dead in the water. Test, verify, then commit.

Also note: we’re disabling root login entirely (prohibit-password means root can’t use passwords, but keys still work). This is the minimum viable security posture. Root over SSH with a key is still risky, so many teams disable it completely and use sudo from a regular user account instead. For now, prohibit-password is acceptable for a small operation, but if you want maximum security, set PermitRootLogin to “no”.

Step 2: UFW Firewall Configuration

UFW is Ubuntu’s simplified firewall wrapper. Set it up:

# Enable UFW
sudo ufw enable

# Allow SSH (critical!)
sudo ufw allow 22/tcp

# Check status
sudo ufw status

Now allow only what OpenClaw needs. By default, deny everything:

sudo ufw default deny incoming
sudo ufw default allow outgoing

This is the principle of least privilege: deny by default, allow explicitly. It’s slightly annoying because you’ll need to add firewall rules later, but it’s also the safest starting point. A misconfiguration that leaves a port open is bad. A misconfiguration that blocks a port you need is annoying but fixable.

Later, when you know what ports OpenClaw needs, add them:

# Example: if OpenClaw listens on 8000
sudo ufw allow 8000/tcp from 1.2.3.4  # Your IP only, not the world

Notice the “from” clause. You’re not opening 8000 to the world—you’re opening it only to your IP. This is another layer of defense. Even if someone discovers the port is open, they still need to be on your network to reach it.

Step 3: fail2ban for Brute Force Protection

Even with good SSH security, bots will hammer your SSH port. fail2ban watches for repeated failures and blocks them:

# Install
sudo apt-get update
sudo apt-get install -y fail2ban

# Copy default config
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Edit and customize
sudo nano /etc/fail2ban/jail.local

Add this at the bottom for aggressive SSH protection:

[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600

This bans IPs that fail 3 times within 10 minutes for 1 hour. Why these numbers? 3 failures is low enough that it’s unlikely to be a legitimate user who forgot their password. 10 minutes is short enough that the attacker doesn’t have time to try many passwords. 1 hour is long enough to be painful for the attacker but short enough that a legitimate user can try again later.

Without fail2ban, your logs will show thousands of SSH brute-force attempts per day. It’s not dangerous (SSH keys can’t be brute-forced), but it’s noise. With fail2ban, those attempts get blocked and your logs stay clean.

# Start fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo fail2ban-client status sshd

Step 4: Unattended Upgrades

Security patches come constantly. Don’t wait for you to manually apply them:

# Install
sudo apt-get install -y unattended-upgrades apt-listchanges

# Enable
sudo dpkg-reconfigure -plow unattended-upgrades

# Verify
sudo systemctl status unattended-upgrades

This automatically installs security updates, reboots if needed, and logs everything. Sleep soundly. The big one: security patches. If a zero-day vulnerability drops for Linux or OpenSSH, you want the patch installed within 24 hours, not months later when you remember to manually update. Unattended-upgrades makes it automatic.

One caveat: some updates require reboots. If you’re running a high-availability setup, reboots are scary. But for a single OpenClaw instance, a 1-minute reboot every two weeks is acceptable. Your users can handle a brief interruption.

Step 5: Create OpenClaw User

Remember the least-privilege article? Let’s apply it:

# Create dedicated user
sudo useradd -r -s /usr/sbin/nologin -d /var/lib/openclaw -m openclaw

# Set up directory structure
sudo mkdir -p /var/lib/openclaw/{config,data,logs,cache,secrets}

# Lock down permissions
sudo chown -R openclaw:openclaw /var/lib/openclaw
sudo chmod 700 /var/lib/openclaw
sudo chmod 700 /var/lib/openclaw/secrets

You’re building a sandbox. OpenClaw runs inside, not as root. This is the principle of least privilege applied to users: OpenClaw doesn’t need root, so OpenClaw doesn’t get root. If a vulnerability in OpenClaw is exploited, the attacker gets access to the openclaw user, not the entire system. That limits the blast radius.

Notice the directory structure: config, data, logs, cache all have tight permissions (700 = read/write/execute only for owner). If someone compromises OpenClaw, they can read the config but can’t pivot to other parts of the system.

Docker Installation and Configuration

Now we get to the good stuff. Docker isolates OpenClaw even further—process-level isolation, networking isolation, filesystem isolation.

Think of the layering: you have a Linux user (openclaw) with limited permissions. Inside that user’s home directory, you run a Docker container. Inside the Docker container, OpenClaw runs with its own filesystem, its own network namespace, its own process namespace. If something goes wrong, the damage is contained at multiple levels. This is defense in depth.

Install Docker

# Add Docker's repository
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# Install
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io

# Verify
docker --version

Add OpenClaw User to Docker Group (Optional)

This lets the openclaw user run Docker without sudo, but be careful—this is a privilege escalation vector. Adding a user to the docker group is almost equivalent to giving them root. Why? Because if you can run Docker, you can mount the host filesystem inside a container and read anything, or run a container as root. It’s a backdoor to privilege escalation.

# Only if you really need it
sudo usermod -aG docker openclaw

For security, I’d skip this and run Docker commands as root through a wrapper script or systemd service. The systemd service runs as root, calls docker-compose, and handles OpenClaw’s lifecycle. The openclaw user never directly runs Docker commands. Best of both worlds: OpenClaw runs inside Docker (isolated) but can’t escalate privileges.

Docker Network Setup

Isolate OpenClaw’s network traffic:

# Create a custom bridge network
docker network create openclaw-net --driver bridge

# View it
docker network ls

Later, when you deploy OpenClaw, you’ll connect it to this network so it can’t accidentally talk to other containers.

Tailscale: Secure VPN Access

Here’s the critical piece: you don’t want OpenClaw exposed to the public internet. You want it accessible only from you. If OpenClaw is listening on 0.0.0.0:8000, anyone on the internet can reach it. If they find an exploit, your server is theirs. If it’s listening on 127.0.0.1:8000, only the local machine can reach it. That’s much safer, but then how do you access it remotely?

Tailscale is a zero-trust VPN built on WireGuard. It’s simple, fast, and free for personal use. It creates a private network that only includes devices you explicitly authorize. Your laptop and your VPS are on the same private network, but they’re the only two devices. No one else can see that network, even if they scan your VPS’s IP address on the public internet.

Install Tailscale on VPS

curl -fsSL https://tailscale.com/install.sh | sh

# Start the service
sudo systemctl enable tailscale
sudo systemctl start tailscale

# Authenticate (opens a browser URL)
sudo tailscale up

This gives you a URL to click. You authenticate with your Tailscale account, and boom—your VPS is part of your private network.

# Check your Tailscale IP
tailscale ip -4

You’ll see something like 100.x.x.x. That’s your private IP across all your devices.

Install Tailscale on Your Local Machine

Do the same on your laptop:

# macOS
brew install tailscale
brew services start tailscale

# Linux
curl -fsSL https://tailscale.com/install.sh | sh
sudo systemctl enable tailscale
sudo systemctl start tailscale

# Windows
# Download from https://tailscale.com/download

Authenticate, and now your laptop and VPS are on the same private network. The key insight: this all happens automatically. Tailscale handles NAT traversal, encryption, and key exchange. You just install it and it works. This is why it’s better than manually configuring WireGuard (which is powerful but tedious).

SSH Through Tailscale

Now instead of SSH-ing to the public IP, SSH through Tailscale:

# Get your VPS's Tailscale IP
ssh -i ~/.ssh/openclaw [email protected]

It’s fast, encrypted end-to-end, and no one on the internet can see your traffic.

Lock Down the VPS Firewall

Since everything’s through Tailscale, you can be aggressive:

# Only allow Tailscale traffic and SSH through Tailscale
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow the Tailscale interface (auto-detects)
sudo ufw allow in on tailscale0

# Allow outbound (for updates, package downloads)
# Already allowed by default

# Check status
sudo ufw status

Now the VPS is unreachable from the public internet unless you’re on your Tailscale network. Perfect. Let’s think about what this means: if a bot scans your VPS’s public IP, it sees a closed firewall. It can’t reach SSH. It can’t reach OpenClaw. It’s like the VPS doesn’t exist to the public internet. Meanwhile, you’re on Tailscale, so you can reach everything immediately. This is the ideal security posture: invisible to attackers, accessible to you.

OpenClaw Deployment with Docker

Now for the main event. We’re going to:

  1. Create a Dockerfile for OpenClaw
  2. Build it
  3. Run it through systemd
  4. Access it through Tailscale

This is where all the hardening comes together. You’ve got a secure server with a locked-down firewall and Tailscale access. Now you’re adding OpenClaw on top, using Docker for additional isolation, and systemd to manage the lifecycle. By the time you’re done, you’ll have a rock-solid foundation for running OpenClaw 24/7.

Create Dockerfile

FROM ubuntu:22.04

# Set environment
ENV DEBIAN_FRONTEND=noninteractive
ENV OPENCLAW_HOME=/var/lib/openclaw

# Install system dependencies
RUN apt-get update && apt-get install -y \
    curl \
    wget \
    git \
    python3 \
    python3-pip \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Create openclaw user
RUN useradd -r -s /usr/sbin/nologin -d /var/lib/openclaw -m openclaw

# Copy your OpenClaw code
COPY openclaw /opt/openclaw
RUN chown -R openclaw:openclaw /opt/openclaw

# Set working directory
WORKDIR /opt/openclaw

# Install dependencies
RUN pip3 install -r requirements.txt

# Create volume mount points
RUN mkdir -p /var/lib/openclaw/{config,data,logs,cache}
RUN chown -R openclaw:openclaw /var/lib/openclaw

# Switch to openclaw user
USER openclaw

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f https://automateanddeploy.com:8000/health || exit 1

# Expose port
EXPOSE 8000

# Run OpenClaw
CMD ["python3", "-m", "openclaw", "--listen", "0.0.0.0:8000"]

Build it:

docker build -t openclaw:latest .

Run via Docker Compose

Create docker-compose.yml:

version: "3.8"

services:
  openclaw:
    image: openclaw:latest
    container_name: openclaw
    restart: always

    networks:
      - openclaw-net

    volumes:
      - /var/lib/openclaw/config:/var/lib/openclaw/config:ro
      - /var/lib/openclaw/data:/var/lib/openclaw/data:rw
      - /var/lib/openclaw/logs:/var/lib/openclaw/logs:rw
      - /var/lib/openclaw/cache:/var/lib/openclaw/cache:rw

    environment:
      - OPENCLAW_CONFIG=/var/lib/openclaw/config/openclaw.conf
      - OPENCLAW_LOG_LEVEL=INFO

    ports:
      - "127.0.0.1:8000:8000" # Listen only on localhost

    # Resource limits
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 1G
        reservations:
          cpus: "0.5"
          memory: 512M

networks:
  openclaw-net:
    driver: bridge

The key line: 127.0.0.1:8000:8000 means OpenClaw only listens on localhost, not on the public interface. Good. This is crucial: Docker containers have their own network namespace. If OpenClaw listens on 0.0.0.0 inside the container, it’s accessible from anywhere inside the Docker network. By mapping it to 127.0.0.1 on the host, you’re saying “only the host machine can reach this port, and only if they’re on localhost.” Combined with the firewall blocking port 8000 entirely, you’ve made OpenClaw invisible to the public internet.

Run via Systemd

Create /etc/systemd/system/openclaw.service:

[Unit]
Description=OpenClaw Service
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
Restart=always
RestartSec=10

# Run as root to manage docker
User=root

# Environment
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
EnvironmentFile=-/var/lib/openclaw/secrets/openclaw.env

# The command
WorkingDirectory=/opt/openclaw
ExecStart=/usr/bin/docker-compose -f /opt/openclaw/docker-compose.yml up

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openclaw

[Install]
WantedBy=multi-user.target

Start it:

sudo systemctl daemon-reload
sudo systemctl enable openclaw
sudo systemctl start openclaw

# Check logs
sudo journalctl -u openclaw -f

Accessing OpenClaw Remotely

Your OpenClaw is now running inside Docker, listening only on localhost, on a VPS you can only reach through Tailscale. How do you actually use it?

Option 1: SSH Tunnel

Create a local port forward through SSH:

ssh -i ~/.ssh/openclaw -L 8000:127.0.0.1:8000 [email protected]

Now visit https://automateanddeploy.com:8000 on your laptop. Traffic goes: laptop → SSH tunnel → VPS OpenClaw → SSH tunnel → laptop. Encrypted end-to-end.

Option 2: Use Tailscale IP Directly

If OpenClaw’s UI supports it, access it directly through Tailscale:

http://100.x.x.x:8000

But this requires network connectivity on the Tailscale interface, which might require firewall tweaks.

Option 3: Reverse Proxy with Nginx

For production, run Nginx as a reverse proxy:

sudo apt-get install -y nginx

# Create /etc/nginx/sites-available/openclaw
sudo nano /etc/nginx/sites-available/openclaw

Add:

server {
    listen 8000;
    server_name 100.x.x.x;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}

Enable it:

sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Now Nginx listens on port 8000 (accessible through Tailscale) and forwards to the Docker container on localhost. Clean separation.

Monitoring and Backups

Your OpenClaw is running. Now make sure it stays healthy. This is where many people drop the ball. They deploy something, it works, and they assume it will keep working forever. Then one day it crashes at 3 AM and nobody notices until morning because there’s no monitoring. Let’s avoid that.

Docker Health Checks

The Dockerfile includes a health check:

docker ps  # Look for "healthy" status
docker inspect openclaw --format='{{json .State.Health}}'

Log Aggregation

OpenClaw logs go to journald via Docker:

# Tail logs
sudo journalctl -u openclaw -f

# Save logs to file
sudo journalctl -u openclaw --no-pager > openclaw-logs.txt

Consider shipping these to a logging service (Logtail, Papertrail) for long-term retention.

Snapshots and Backups

Set up automated snapshots (all three providers support this):

Hetzner:

# Via API
hcloud volume-action attach $VOLUME_ID $SERVER_ID

DigitalOcean:

  • Dashboard → Droplets → Backups → Enable automated backups

Linode:

  • Dashboard → Linodes → Settings → Backup → Enable Backups

Backup at least weekly. Test restores quarterly. Here’s the dirty secret: backups are worthless if you’ve never restored from them. You might have a backup file, but if it’s corrupted or incomplete, you won’t know until you actually try to restore. Set a calendar reminder every quarter to restore a backup to a test server and verify it works. This takes 30 minutes and could save your bacon when disaster strikes.

Migrating Between Providers: From Hetzner to DigitalOcean (and Back)

Six months into running OpenClaw on Hetzner, you realize DigitalOcean’s observability features are worth the extra cost. Or vice versa. Migration between providers is straightforward if you plan it right.

The good news: thanks to Docker and the isolation you’ve already set up, migration is portable. You’re not moving a physical server. You’re moving a set of files (Docker images, config, data). You could even move to a totally different provider if Hetzner ever disappoints you.

Pre-Migration Checklist:

  1. Full backup of your VPS (snapshots from the source provider)
  2. Export all OpenClaw config and data
  3. Note your current server specs, DNS entries, and firewall rules
  4. Identify all external dependencies (databases, APIs, third-party services)
  5. Maintenance window scheduled (ideally off-peak)

Step 1: Export Configuration and Data

On your current VPS:

# Export Docker volumes
docker run --rm \
  -v openclaw_data:/data \
  -v $(pwd)/backup:/backup \
  alpine tar czf /backup/openclaw-data.tar.gz -C /data .

# Export systemd service file
sudo cp /etc/systemd/system/openclaw.service ~/openclaw.service

# Export docker-compose file
cp docker-compose.yml ~/docker-compose.yml

# Export environment variables
cp /var/lib/openclaw/secrets/openclaw.env ~/openclaw.env

# Create a snapshot on source provider
# Hetzner: via dashboard or hcloud CLI
# DigitalOcean: via dashboard
# Linode: via dashboard

Step 2: Provision New VPS at Target Provider

Repeat the “Initial Server Setup” section with your new provider. Take careful notes on:

  • New server IP address
  • SSH key configuration
  • Firewall rules

Step 3: Restore Data on New VPS

On your new VPS:

# Copy backup files from old server
scp -i ~/.ssh/openclaw root@OLD_SERVER_IP:~/openclaw-data.tar.gz .
scp -i ~/.ssh/openclaw root@OLD_SERVER_IP:~/docker-compose.yml .
scp -i ~/.ssh/openclaw root@OLD_SERVER_IP:~/openclaw.service .

# Restore Docker volume
docker volume create openclaw_data
docker run --rm \
  -v openclaw_data:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/openclaw-data.tar.gz -C /data

# Restore systemd service
sudo cp openclaw.service /etc/systemd/system/

# Restore environment
sudo cp openclaw.env /var/lib/openclaw/secrets/

# Update permissions
sudo chown -R openclaw:openclaw /var/lib/openclaw

Step 4: Reconfigure Networking

Each provider’s network naming is different:

# On new VPS, check Tailscale configuration
sudo tailscale up --reset  # Fresh authentication

# Get new Tailscale IP
tailscale ip -4

# Update any DNS records pointing to your old server
# Example: if you had gateway.openclaw.internal → OLD_IP
# Update to: gateway.openclaw.internal → NEW_TAILSCALE_IP

# Or use your provider's public IP if not using Tailscale

Step 5: Verify and Cutover

# Start OpenClaw on new server
sudo systemctl start openclaw
sudo systemctl status openclaw

# Verify it's healthy
docker ps  # Should show "healthy"
docker logs openclaw  # Should show no errors

# Test from your laptop through Tailscale
curl http://NEW_TAILSCALE_IP:8000/api/health

# If using DNS, test that too
curl http://gateway.openclaw.internal:8000/api/health

Step 6: Update Clients

If clients are hardcoding IPs, update them to point to the new server. If using DNS, just wait for cache expiration (or force refresh).

# Announce the change internally
# "OpenClaw is now on NEW_IP. Old server decommissioned on [date]."

Step 7: Decommission Old Server

Only after you’ve confirmed everything works for 24+ hours:

# On old server, backup one more time
sudo systemctl stop openclaw
docker run --rm \
  -v openclaw_data:/data \
  -v $(pwd)/final-backup:/backup \
  alpine tar czf /backup/final-openclaw-backup.tar.gz -C /data .

# Copy final backup to safe location
scp -i ~/.ssh/openclaw root@NEW_SERVER_IP:~/final-openclaw-backup.tar.gz ~/backups/

# Destroy the old VPS through provider dashboard
# (Don't use CLI if you can—human confirmation is safer)

Total Migration Time: 30 minutes to 1 hour, including cutover and verification. This is a remarkably short time because you’ve already containerized everything. If you were running OpenClaw directly on the OS with custom configs scattered everywhere, this would take days. Docker and version control make it fast.

Cost: $10-15 if you pay for both servers for a month. Use this time to test that the new provider actually feels better before committing. I’d recommend keeping the old server running for a week while the new one handles production. If something goes wrong, you can pivot back in minutes instead of hours.

Pro tip: set up CI/CD or Infrastructure-as-Code (Terraform/Pulumi) from day one, and migrations become one command instead of a manual checklist. If you define your entire infrastructure in code, spinning up a new server becomes automatic. It’s more work upfront but saves enormous time later.

The Full Deployment Checklist

Before you sleep peacefully:

  • [ ] VPS provisioned with SSH key only
  • [ ] UFW firewall configured (SSH, Tailscale only)
  • [ ] fail2ban installed and active
  • [ ] Unattended-upgrades enabled
  • [ ] OpenClaw user created with limited permissions
  • [ ] Docker installed and configured
  • [ ] Tailscale installed on VPS and local machine
  • [ ] VPS unreachable from public internet (firewall only allows Tailscale/SSH)
  • [ ] OpenClaw running in Docker via docker-compose
  • [ ] systemd service configured and running
  • [ ] Nginx reverse proxy (optional, but recommended for production)
  • [ ] Snapshots/backups enabled
  • [ ] Logs accessible and monitored
  • [ ] Health checks passing
  • [ ] Access tested through Tailscale tunnel

One server. Secure. Automated. Running OpenClaw 24/7.

You’ve built something real here. Not a toy on your laptop that shuts down when you close it. A production system that scales, backs up automatically, and isolates OpenClaw from the rest of the world. You’ve layered security (SSH keys, firewall, fail2ban, Tailscale, Docker), you’ve planned for growth (benchmarking, monitoring, backups), and you’ve kept yourself sane with Tailscale VPN access instead of exposing everything to the internet.

This is what professional infrastructure looks like. Not expensive, not complex, just disciplined.

Welcome to the cloud.

Free Discovery Call

Start With a Conversation, Not a Commitment

Every engagement begins with a free 30-minute discovery call. We'll map what's slowing your business down and tell you exactly what we'd fix first – no pitch deck, no obligation.