Coming soon: Dedicated VPS hardening guide with cloud-init scripts, firewall rules, and automated lockdown procedures (~Week 2-3 on content calendar).
Quick Start (If You Accept the Risks)
1
2
3
4
5
6
7
8
9
# Clone the compose configurationcurl -fsSL https://raw.githubusercontent.com/openclaw/openclaw/main/docker-compose.yml -o docker-compose.yml
# Create environment filecp .env.example .env
# Edit .env with your API keys and settings# Start with security flagsdocker-compose up -d
version:'3.8'services:openclaw:image:openclaw/openclaw:latestcontainer_name:openclaw-agentrestart:unless-stopped# Security: Run as non-rootuser:"1000:1000"# Security: Read-only root filesystemread_only:true# Security: Limit capabilitiescap_drop:- ALLcap_add:- NET_BIND_SERVICE # Only if binding to privileged port# Security: No new privilegessecurity_opt:- no-new-privileges:true# Security: Limited resourcesdeploy:resources:limits:cpus:'2.0'memory:2Greservations:cpus:'0.5'memory:512M# Network: Custom isolated networknetworks:- openclaw-net# Ports: Only expose what you need# WARNING: Binding to 0.0.0.0 exposes to the internet# Use 127.0.0.1:port:port for localhost-onlyports:- "127.0.0.1:3000:3000"# Web UI - localhost only- "127.0.0.1:8080:8080"# Gateway API - localhost only# Environment: Pass through, not hardcodedenv_file:- .env# Volumes: Minimal, temporary where possiblevolumes:# Persistent data (encrypted at rest ideally)- openclaw-data:/app/data# Temporary files (noexec, nosuid, nodev)- type:tmpfstarget:/tmptmpfs:size:100Mmode:1777# Read-only config- ./config:/app/config:ro# Health checkshealthcheck:test:["CMD","curl","-f","http://localhost:8080/health"]interval:30stimeout:10sretries:3start_period:40s# Logginglogging:driver:"json-file"options:max-size:"10m"max-file:"3"labels:"openclaw,agent"# Optional: Monitoring sidecaragent-monitor:image:prom/node-exporter:latestcontainer_name:openclaw-monitorrestart:unless-stoppednetworks:- openclaw-netvolumes:- /proc:/host/proc:ro- /sys:/host/sys:ro- /:/rootfs:rocommand:- '--path.procfs=/host/proc'- '--path.rootfs=/rootfs'- '--path.sysfs=/host/sys'- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'networks:openclaw-net:driver:bridgeinternal:false# Set to true for no external access (use with proxy)ipam:config:- subnet:172.20.0.0/16volumes:openclaw-data:driver:local
# ============================================================# RECOMMENDED: Gemma 4 primary + Kimi k2.5 fallback (both free tiers)# ============================================================# Primary: Gemma 4 (Google free tier)DEFAULT_MODEL=gemini-2.5-pro-exp-03-25
GEMINI_API_KEY=your-gemini-key
# Fallback: Kimi k2.5 via NVIDIA NIM (free trial credits)FALLBACK_MODEL=moonshotai/kimi-k2-5
NVIDIA_API_KEY=nvapi-your-key-here
NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
# Alternative fallback: Qwen 3.6 via OpenRouter (1M context, free tier)# OPENROUTER_API_KEY=sk-or-your-key# OPENROUTER_BASE_URL=https://openrouter.ai/api/v1# QWEN_MODEL=qwen/qwen3.6-plus-preview:free# ============================================================# Security: Disable dangerous features by default# ============================================================ENABLE_SHELL_EXECUTION=falseENABLE_FILE_WRITE=falseENABLE_NETWORK_FETCH=false# Only enable after reviewing skill code:# ENABLE_SHELL_EXECUTION=true# ENABLE_FILE_WRITE=true# ENABLE_NETWORK_FETCH=true# LoggingLOG_LEVEL=info
LOG_FORMAT=json
# Gateway binding (127.0.0.1 = localhost only)GATEWAY_BIND=127.0.0.1
GATEWAY_PORT=8080
Network Security Checklist
Before Starting the Container
Firewall configured: Only outbound 443, no inbound except SSH
Gateway binds to localhost: 127.0.0.1:port, not 0.0.0.0:port
No host network mode: Never use --network host
Secrets in env file: Not in compose, not in images
VPS off home network: Hetzner/Vultr, not your laptop
After Starting the Container
1
2
3
4
5
6
7
8
9
10
11
12
13
# Verify network exposuress -tlnp | grep -E '(3000|8080)'# Should show 127.0.0.1, NOT 0.0.0.0 or :::# Check for unexpected portsdocker exec openclaw-agent ss -tlnp
# Verify read-only filesystemdocker exec openclaw-agent touch /tmp/test-write # Should workdocker exec openclaw-agent touch /app/test-write # Should fail# Check running userdocker exec openclaw-agent id # Should show uid=1000, not root
Common Mistakes
Mistake 1: --privileged Mode
Don’t:
1
docker run --privileged openclaw/openclaw # Gives full host access
Do: Use capability dropping (see compose file above).