Deploy OpenClaw to VPS: Production Setup Tutorial
Deploy OpenClaw to VPS: Production Setup Tutorial

Most tutorials about deploying AI agents to a VPS read like they were written by someone who's never actually SSHed into a fresh Ubuntu box at 11pm trying to figure out why their agent crashed for the third time. They give you the "hello world" and leave you stranded the moment something real goes wrong.
This isn't that tutorial.
I'm going to walk you through deploying OpenClaw to a VPS from scratch — the actual production setup, not the demo-day version. We'll cover server selection, environment configuration, persistent agent processes, monitoring, security, and the dozen small things that separate a toy deployment from something you can walk away from and trust to keep running.
If you're new to OpenClaw entirely, I'd recommend grabbing Felix's OpenClaw Starter Pack before diving in. It bundles pre-configured agent templates, deployment scripts, and environment files that eliminate about two hours of setup boilerplate. I'll reference some of its components throughout this guide, but you can follow along without it — just expect to write more config files by hand.
Let's get into it.
Why a VPS (and Not Your Laptop)
Running OpenClaw locally is fine for development. It's terrible for production. Here's why:
Your laptop sleeps. Your agents shouldn't. The entire point of deploying autonomous agents is that they work when you don't. The moment your MacBook lid closes or your Wi-Fi hiccups, your agent's mid-task state evaporates.
Resource contention is real. Open a few browser tabs, a Slack call, and an OpenClaw agent doing heavy tool use simultaneously, and you'll watch your system grind to a halt. A VPS gives your agents dedicated, predictable compute.
Networking matters. VPS providers give you static IPs, predictable egress, and uptime SLAs. If your agent is calling APIs, scraping data, or communicating with webhooks, you need a stable network identity — not your home ISP's dynamic IP behind a consumer NAT.
Security isolation. Your development machine has your SSH keys, browser sessions, password managers, and personal files. Running autonomous agents with tool access on that same machine is a risk surface you don't need.
A $10-20/month VPS solves all of this.
Step 1: Choosing a VPS Provider
For most OpenClaw deployments, you don't need a GPU. OpenClaw's architecture is designed to orchestrate agent logic and tool use efficiently — the heavy LLM inference happens via API calls, not local model weights (unless you specifically configure local model support, which we'll touch on later).
Here's what I recommend based on workload:
For most people: Hetzner Cloud. Best price-to-performance ratio in the industry. A CX31 (4 vCPU, 8GB RAM) runs about €7.50/month and handles multiple concurrent OpenClaw agents comfortably. Their Nuremberg and Helsinki data centers have excellent connectivity.
If you need US-based servers: DigitalOcean or Vultr. Slightly more expensive than Hetzner, but solid. A 4GB/2vCPU droplet at $24/month works fine for small deployments.
If you're running local models alongside OpenClaw: RunPod or Vast.ai. These give you GPU instances (A4000, A5000, etc.) at reasonable hourly rates. Only go this route if you have a specific reason to run inference locally.
Avoid for this use case: AWS EC2 and GCP Compute Engine, unless your org mandates them. You'll pay 3-5x more for equivalent resources and spend hours navigating IAM policies instead of actually deploying.
For this tutorial, I'll use a fresh Hetzner CX31 running Ubuntu 24.04 LTS.
Step 2: Initial Server Setup
SSH into your fresh box:
ssh root@your-server-ip
First, do the basics that every VPS tutorial covers but that matter enormously:
# Update everything
apt update && apt upgrade -y
# Create a non-root user
adduser deploy
usermod -aG sudo deploy
# Set up SSH key auth for the new user
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
# Disable root login and password auth
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
Now set up a basic firewall:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
Log out, log back in as your deploy user, and confirm everything works before moving on. Locking yourself out of a VPS because you misconfigured SSH is a rite of passage, but let's skip it today.
Step 3: Install Docker and Docker Compose
OpenClaw's recommended deployment method is containerized. Docker gives you reproducible environments, easy rollbacks, and process isolation — all things you desperately want when running autonomous agents.
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker deploy
# Log out and back in to pick up the group change
exit
# SSH back in as deploy
# Verify
docker run hello-world
# Install Docker Compose plugin
sudo apt install docker-compose-plugin -y
docker compose version
Step 4: Deploy OpenClaw
Create your project directory and set up the deployment structure:
mkdir -p ~/openclaw-deploy && cd ~/openclaw-deploy
Create your docker-compose.yml:
version: "3.8"
services:
openclaw:
image: openclaw/openclaw:latest
container_name: openclaw-agent
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./config:/app/config
- ./data:/app/data
- ./logs:/app/logs
env_file:
- .env
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 1G
redis:
image: redis:7-alpine
container_name: openclaw-redis
restart: unless-stopped
volumes:
- redis-data:/data
command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
volumes:
redis-data:
A few things to notice here:
restart: unless-stopped— This is critical. When your agent crashes (and it will, eventually), Docker restarts it automatically. This single line is the difference between "my agent ran for 6 months" and "my agent died at 2am on a Saturday and I didn't notice until Monday."- Memory limits — Set them. OpenClaw agents that loop unexpectedly can consume all available RAM and crash your entire VPS. The
limitsfield acts as a hard ceiling. - Redis for state persistence — OpenClaw uses Redis to maintain agent memory and task state across restarts. The
appendonly yesflag ensures data survives container restarts. - Health checks — Docker will monitor the health endpoint and restart the container if it becomes unresponsive.
Now create your .env file:
# .env
OPENCLAW_API_KEY=your-openclaw-api-key-here
OPENCLAW_AGENT_MODE=production
OPENCLAW_LOG_LEVEL=info
OPENCLAW_REDIS_URL=redis://redis:6379
OPENCLAW_MAX_ITERATIONS=1000
OPENCLAW_COST_LIMIT_USD=50.00
OPENCLAW_WEBHOOK_URL=https://your-webhook-endpoint.com/alerts
Two settings that will save you money and headaches:
OPENCLAW_MAX_ITERATIONS prevents runaway agent loops. Without this, a poorly configured agent can spin forever, burning through API credits. Set a sane upper bound based on your use case.
OPENCLAW_COST_LIMIT_USD is your financial circuit breaker. When cumulative API costs hit this threshold, OpenClaw pauses execution and alerts you. Start conservative, raise it as you build confidence.
If you picked up Felix's OpenClaw Starter Pack, it includes a production-ready .env.example with sensible defaults for all of these, plus additional configuration for rate limiting and retry policies that aren't obvious from the docs.
Step 5: Agent Configuration
Create your agent config directory and add your first agent:
mkdir -p config
Create config/agents.yml:
agents:
- name: research-agent
description: "Monitors industry sources and produces daily briefings"
schedule: "0 6 * * *" # Run daily at 6am UTC
tools:
- web_search
- url_reader
- file_writer
memory:
type: persistent
backend: redis
ttl: 604800 # 7 days
output:
format: markdown
destination: /app/data/briefings/
error_handling:
max_retries: 3
retry_delay: 60
on_failure: pause_and_alert
- name: data-processor
description: "Processes incoming data files and generates reports"
trigger: webhook
tools:
- file_reader
- data_analyzer
- file_writer
memory:
type: session
backend: redis
output:
format: json
destination: /app/data/reports/
error_handling:
max_retries: 2
retry_delay: 30
on_failure: log_and_continue
The key decisions in agent configuration:
Schedule vs. webhook triggers. Scheduled agents run on cron timing — great for daily reports, periodic monitoring, recurring research tasks. Webhook-triggered agents fire in response to external events — ideal for processing incoming data, responding to alerts, or chaining with other systems.
Memory type matters. persistent memory carries across sessions, meaning your agent remembers previous runs. Use this for agents that build knowledge over time. session memory resets each run — better for stateless data processing tasks where you want clean execution.
Error handling isn't optional. pause_and_alert stops the agent and notifies you. log_and_continue records the failure and moves on. Choose based on how critical the task is. A daily news briefing can probably tolerate a skipped item; a financial data pipeline probably can't.
Step 6: Fire It Up
cd ~/openclaw-deploy
docker compose up -d
Check that everything is running:
docker compose ps
docker compose logs -f openclaw
You should see OpenClaw initialize, load your agent configurations, connect to Redis, and report ready status. If something fails, the logs will tell you exactly what — usually a missing environment variable or a malformed config file.
Test the health endpoint:
curl http://localhost:8080/health
You should get back a JSON response confirming agent status, memory connection, and system resources.
Step 7: Reverse Proxy and SSL
Don't expose port 8080 directly to the internet. Put Nginx in front of it with SSL:
sudo apt install nginx certbot python3-certbot-nginx -y
Create /etc/nginx/sites-available/openclaw:
server {
server_name agents.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
# WebSocket support for real-time agent monitoring
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable and secure it:
sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d agents.yourdomain.com
Now your OpenClaw dashboard and API are accessible over HTTPS with auto-renewing certificates.
Step 8: Monitoring and Logging
Running agents without monitoring is like driving without a dashboard. You need to know what's happening.
Structured logging:
OpenClaw writes structured JSON logs to the ./logs directory we mounted. Set up log rotation so they don't fill your disk:
sudo tee /etc/logrotate.d/openclaw << 'EOF'
/home/deploy/openclaw-deploy/logs/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
EOF
Simple uptime monitoring:
For most deployments, a lightweight solution beats a full observability stack. Create a simple monitoring script at ~/openclaw-deploy/monitor.sh:
#!/bin/bash
HEALTH_URL="http://localhost:8080/health"
WEBHOOK_URL="${OPENCLAW_WEBHOOK_URL}"
response=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)
if [ "$response" != "200" ]; then
curl -X POST $WEBHOOK_URL \
-H "Content-Type: application/json" \
-d "{\"text\": \"⚠️ OpenClaw health check failed. HTTP status: $response\"}"
fi
chmod +x ~/openclaw-deploy/monitor.sh
# Run every 5 minutes
(crontab -l 2>/dev/null; echo "*/5 * * * * /home/deploy/openclaw-deploy/monitor.sh") | crontab -
This catches the edge cases that Docker's built-in health checks miss — like when the container is "running" but the agent is deadlocked or the API is returning errors.
Step 9: Backups and Disaster Recovery
Your agents accumulate state, output files, and configuration that you don't want to lose. Set up automated backups:
#!/bin/bash
# ~/openclaw-deploy/backup.sh
BACKUP_DIR="/home/deploy/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup config and data
tar -czf $BACKUP_DIR/openclaw-backup-$TIMESTAMP.tar.gz \
-C /home/deploy/openclaw-deploy \
config/ data/ .env docker-compose.yml
# Backup Redis
docker exec openclaw-redis redis-cli BGSAVE
sleep 5
docker cp openclaw-redis:/data/dump.rdb $BACKUP_DIR/redis-$TIMESTAMP.rdb
# Keep only last 7 days
find $BACKUP_DIR -mtime +7 -delete
Run this daily via cron. For off-server backups, rsync the backup directory to a second VPS, an S3 bucket, or even a local machine.
Step 10: Updates and Maintenance
Updating OpenClaw is straightforward with Docker:
cd ~/openclaw-deploy
# Pull the latest image
docker compose pull
# Restart with the new image
docker compose up -d
# Verify
docker compose logs -f openclaw
The unless-stopped restart policy combined with Redis-backed state persistence means your agents pick up where they left off after an update. No lost context, no orphaned tasks.
Pro tip: Before updating production, test on a staging VPS first. Spin up a $5 Hetzner box, clone your config (with lower cost limits), run the new version for a day, then promote to production. This costs almost nothing and saves you from deploying a broken update to agents that handle real work.
Common Gotchas and How to Fix Them
"My agent works locally but fails on the VPS." Usually a DNS or networking issue. VPS default resolvers can be slow or unreliable. Add Google's DNS to your Docker daemon config:
// /etc/docker/daemon.json
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
Restart Docker after: sudo systemctl restart docker
"Agent memory keeps resetting."
Check that your Redis volume is properly mounted and that appendonly is enabled. Run docker exec openclaw-redis redis-cli DBSIZE to verify data is actually being stored.
"I'm burning through API credits way too fast."
Lower OPENCLAW_MAX_ITERATIONS, set a tighter OPENCLAW_COST_LIMIT_USD, and review your agent's tool configuration. Agents with unrestricted web search access are the most common culprit — they'll search dozens of queries per task if you don't constrain them.
"The VPS runs out of disk space." Docker images and logs are the usual suspects. Clean up regularly:
docker system prune -a --volumes # Nuclear option — removes unused everything
Or more surgically, just prune old images and set up the log rotation described above.
Where to Go From Here
You now have a production OpenClaw deployment that's secure, monitored, persistent, and cost-controlled. That's more than most people ever set up, and it's enough to run meaningful autonomous agents 24/7.
Next steps worth exploring:
- Multi-agent orchestration — Configure agents that hand off tasks to each other using OpenClaw's built-in communication layer
- Custom tool development — Build tools specific to your domain that your agents can invoke
- Webhook integrations — Connect your agents to Slack, email, or other notification systems for real-time output delivery
- Scaling — When one VPS isn't enough, OpenClaw supports distributed deployments across multiple nodes
If you haven't already, seriously consider Felix's OpenClaw Starter Pack. The production deployment templates alone save you a couple of hours of config writing, and the pre-built agent examples give you a running start on common use cases like research automation, data monitoring, and content processing. It's the fastest way to go from "I have a VPS" to "I have agents doing useful work."
Now stop reading and go deploy something.