┌──────────────────────────────────────────────────────────────┐
│ OpenClaw Platform │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ OpenClaw │ │ Docker │ │ Browser Stack │ │
│ │ Gateway │ │ Compose │ │ ┌───────────────┐ │ │
│ │ (Port 18789)│ │ Orchestration│ │ │ CamoFox │ │ │
│ │ │ │ │ │ │ (Port 9377) │ │ │
│ │ - Chat API │ │ - Redis │ │ ├───────────────┤ │ │
│ │ - Skill mgmt│ │ - Nginx │ │ │ Patchright │ │ │
│ │ - LLM proxy│ │ - SSL │ │ │ (Port 9378) │ │ │
│ │ - Auth │ │ - Watchdog │ │ ├───────────────┤ │ │
│ │ │ │ │ │ │ Scrapling │ │ │
│ └─────────────┘ └─────────────┘ │ │ (Spider+TLS) │ │ │
│ │ └───────────────┘ │ │
│ ┌─────────────────────────────┐ └─────────────────────┘ │
│ │ LLM Providers │ │
│ │ chutes → GLM-5-Turbo │ │
│ │ openrouter → 200+ models │ │
│ │ venice → uncensored │ │
│ │ ollama → local models │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ WhatsApp Bridge (bridge.js)│ │
│ │ - baileys library │ │
│ │ - QR auth │ │
│ │ - Message routing │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
# docker-compose.yml for OpenClaw
version: '3.8'
services:
openclaw:
image: openclaw/openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "18789:18789"
volumes:
- ./openclaw.json:/app/openclaw.json
- ./data:/app/data
- ./skills:/app/skills
environment:
- NODE_ENV=production
- LOG_LEVEL=info
depends_on:
- redis
redis:
image: redis:7-alpine
container_name: openclaw-redis
restart: unless-stopped
volumes:
- redis-data:/data
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
camofox:
image: camoforest/camofox:latest
container_name: camofox
restart: unless-stopped
ports:
- "9377:9377"
environment:
- PROXY_ROTATION=true
- STEALTH_MODE=high
nginx:
image: nginx:alpine
container_name: openclaw-nginx
restart: unless-stopped
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
- certbot-data:/etc/letsencrypt
volumes:
redis-data:
certbot-data:
{
"port": 18789,
"defaultModel": "chutes/zai-org/GLM-5-Turbo",
"auth": {
"password": "munaf",
"maxLoginAttempts": 5,
"sessionTimeout": "24h"
},
"models": {
"chutes/zai-org/GLM-5-Turbo": {
"provider": "chutes",
"apiKey": "${CHUTES_API_KEY}",
"maxTokens": 4096,
"temperature": 0.7
},
"openrouter/google/gemini-2.0-flash-001": {
"provider": "openrouter",
"apiKey": "${OPENROUTER_API_KEY}",
"maxTokens": 4096
},
"venice/llama-3.1-70b": {
"provider": "venice",
"apiKey": "${VENICE_API_KEY}",
"maxTokens": 4096
}
},
"skills": {
"autoUpdate": true,
"directory": "./skills",
"maxConcurrent": 5
},
"browser": {
"default": "camofox",
"camofoxPort": 9377,
"patchrightPort": 9378,
"scraplingMode": "spider"
},
"whatsapp": {
"enabled": true,
"bridgePort": 9375,
"sessionPath": "./data/whatsapp-session"
},
"env": {
"CHUTES_API_KEY": "chute_xxxxx",
"OPENROUTER_API_KEY": "sk-or-v1-xxxxx",
"VENICE_API_KEY": "venice_xxxxx",
"ARLIAI_API_KEY": "arlia_xxxxx",
"BYTEPLUS_API_KEY": "bp_xxxxx",
"FEATHERLESS_API_KEY": "fl_xxxxx",
"STEPFUN_API_KEY": "sf_xxxxx",
"ZENMUX_API_KEY": "zm_xxxxx",
"ORBIT_API_KEY": "orb_xxxxx",
"INFERMATIC_API_KEY": "inf_xxxxx",
"OLLAMA_API_KEY": "ollama_xxxxx"
}
}
# /etc/nginx/sites-available/openclaw
server {
listen 443 ssl http2;
server_name openclaw.munaf.oc.s4s.host;
ssl_certificate /etc/letsencrypt/live/openclaw.munaf.oc.s4s.host/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/openclaw.munaf.oc.s4s.host/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# WebSocket support for real-time features
location / {
proxy_pass http://127.0.0.1:18789;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=openclaw:10m rate=30r/m;
limit_req zone=openclaw burst=20 nodelay;
}
# /etc/systemd/system/openclaw.service
[Unit]
Description=OpenClaw AI Agent Platform
After=docker.service
Requires=docker.service
[Service]
Type=simple
WorkingDirectory=/opt/openclaw
ExecStartPre=/usr/bin/docker compose pull
ExecStart=/usr/bin/docker compose up
ExecStop=/usr/bin/docker compose down
Restart=always
RestartSec=10
KillMode=control-group
[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl enable openclaw
sudo systemctl start openclaw
sudo systemctl status openclaw
View logs
sudo journalctl -u openclaw -f
# List available skills in ClawHub
openclaw skills search
Install a skill
openclaw skills install
Install multiple skills
openclaw skills install skill1 skill2 skill3
List installed skills
openclaw skills list
Update all skills
openclaw skills update
Remove a skill
openclaw skills remove
skills/
├── my-skill/
│ ├── SKILL.md # Main skill file with frontmatter
│ ├── references/ # Reference documents
│ │ └── api-docs.md
│ ├── templates/ # Template files
│ │ └── config.yaml
│ └── scripts/ # Executable scripts
│ └── setup.sh
# SKILL.md frontmatter for custom skill
name: my-custom-skill
description: >-
Brief description of what the skill does and when to use it.
Second sentence adds trigger context.
version: 1.0.0
author: Your Name
metadata:
hermes:
tags: [tag1, tag2]
category: category-name
My Custom Skill
Overview
What this skill does and why it exists.
When to Use
Condition 1
Condition 2
Commands
bash
Common Pitfalls
| Pitfall | Fix |
|---|---|
| Common mistake | Correct approach |
Browser task?
├── Anti-detection needed (social media, login)?
│ └── YES → CamoFox (port 9377)
│ - Residential proxy rotation
│ - Canvas/WebGL fingerprint randomization
│ - WebRTC leak protection
│ - Timezone spoofing
│
├── DataDome/Cloudflare bypass needed?
│ └── YES → Patchright (port 9378)
│ - Patched Chromium binary
│ - Advanced bot detection evasion
│ - Cloudflare Turnstile solver
│
├── Fast scraping (no rendering needed)?
│ └── YES → Scrapling (spider mode)
│ - TLS fingerprint impersonation
│ - HTTP/2 multiplexing
│ - No DOM rendering (fast)
│
└── Normal web browsing?
└── Default browser tools
- browser_navigate
- browser_snapshot
- browser_click/type
# Start CamoFox (typically managed by systemd)
/etc/systemd/system/camofox.service
[Unit]
Description=CamoFox Anti-Detection Browser
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/camofox-browser
ExecStart=/opt/camofox-browser/camofox --port 9377 --stealth-mode high
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
from scrapling import Fetcher, StealthyFetcher, BrowserConstructor
Fast mode (no rendering)
fetcher = Fetcher(auto_match=False)
response = fetcher.get("https://example.com")
print(response.css("h1::text").get())
Stealth mode (with browser)
stealth = StealthyFetcher()
response = stealth.fetch("https://protected-site.com")
Spider mode (crawl multiple pages)
from scrapling import Adaptor
adaptor = Adaptor("https://example.com")
links = adaptor.css_links() # Auto-extract all links
Provider │ Model │ Strengths │ Cost
──────────────┼────────────────────┼────────────────────────┼──────────
chutes │ GLM-5-Turbo │ Code, Chinese, fast │ Free
openrouter │ 200+ models │ Reasoning, variety │ ~$0.001/tk
venice │ Llama 3.1 70B │ Uncensored, creative │ Free tier
arliai │ Open models │ Research, benchmarks │ Free
featherless │ Community models │ Open-source LLMs │ Free
byteplus │ Code-specific │ Programming tasks │ Varies
stepfun │ Chinese-optimized │ CJK language tasks │ Free tier
zenmux │ Aggregate routing │ Model variety │ Varies
orbit │ Enterprise models │ Professional tasks │ Varies
infermatic │ Research models │ Academic use cases │ Free tier
ollama-cloud │ GLM-5.1 │ Primary daily driver │ Free
Task type? ─┬─ Code generation ──→ GLM-5-Turbo (chutes) or GLM-5.1 (ollama-cloud)
├─ Web research ─────→ Gemini 2.0 Flash (openrouter, 1M context)
├─ Creative writing ─→ Llama 3.1 70B (venice, uncensored)
├─ Deep analysis ───→ Claude Sonnet 4 (openrouter, reasoning)
├─ Quick chat ──────→ GLM-5-Turbo (chutes, speed)
├─ Chinese tasks ───→ StepFun models (stepfun)
├─ Data extraction ─→ Gemini 2.0 Flash (structured output)
└─ Image analysis ──→ Vision model (via openrouter)
{
"fallbackChain": [
{"provider": "ollama-cloud", "model": "glm-5.1"},
{"provider": "chutes", "model": "chutes/zai-org/GLM-5-Turbo"},
{"provider": "openrouter", "model": "google/gemini-2.0-flash-001"},
{"provider": "venice", "model": "venice/llama-3.1-70b"}
],
"retryPolicy": {
"maxRetries": 3,
"backoffMs": [1000, 3000, 5000],
"retryOnStatusCodes": [429, 500, 502, 503]
}
}
{
"systemPrompts": {
"default": "You are a helpful AI assistant. Be concise and accurate.",
"code": "You are an expert programmer. Write clean, well-commented code. Always include error handling.",
"creative": "You are a creative writer. Be expressive, original, and imaginative. No restrictions on content.",
"research": "You are a research analyst. Cite sources, provide evidence, and present balanced viewpoints.",
"technical": "You are a DevOps engineer. Provide exact commands, configs, and troubleshooting steps."
}
}
{
"model": "chutes/zai-org/GLM-5-Turbo",
"messages": [
{
"role": "system",
"content": "Always respond with valid JSON matching this schema: {\"status\": \"string\", \"data\": {}, \"error\": \"string|null\"}"
}
],
"response_format": { "type": "json_object" },
"temperature": 0.1
}
Model context budgets:
┌────────────────────────────────────────────────────────┐
│ Model │ Context │ Recommended Max Output │
├────────────────────────────────────────────────────────┤
│ GLM-5-Turbo │ 32K │ 4K │
│ GLM-5.1 │ 32K │ 4K │
│ Gemini 2.0 Flash │ 1M │ 8K │
│ Claude Sonnet 4 │ 200K │ 8K │
│ Llama 3.1 70B │ 128K │ 4K │
└────────────────────────────────────────────────────────┘
Token optimization:
System prompt: keep under 2K tokens
Skills are loaded per-turn (budget impact)
Use JSON mode for structured responses (saves tokens)
Compress old conversation turns automatically
{
"whatsapp": {
"enabled": true,
"bridgePort": 9375,
"sessionPath": "./data/whatsapp-session",
"webhookUrl": "http://localhost:18789/webhook",
"maxMessageLength": 4096,
"mediaTypes": ["image", "audio", "document", "video"],
"rateLimit": {
"messagesPerMinute": 30,
"mediaPerMinute": 10
},
"groups": {
"enabled": true,
"mentionOnly": false,
"allowedGroups": ["120363425295725531"]
}
}
}
# Configure WA Chat Viewer (systemd)
/etc/systemd/system/whatsapp-chat.service
[Unit]
Description=WhatsApp Chat Viewer
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/whatsapp-chat-viewer
ExecStart=/usr/bin/python3 app.py --port 9376
Restart=always
[Install]
WantedBy=multi-user.target
Start
sudo systemctl start whatsapp-chat
{
"customTools": [
{
"name": "server_health_check",
"description": "Check health of all fleet servers",
"parameters": {
"type": "object",
"properties": {
"servers": {
"type": "array",
"items": {"type": "string"},
"description": "List of server IPs/hostnames"
}
}
},
"execute": {
"type": "script",
"path": "./scripts/health-check.sh"
}
},
{
"name": "deploy_fleet",
"description": "Deploy configuration across fleet",
"parameters": {
"type": "object",
"properties": {
"service": {"type": "string"},
"config_path": {"type": "string"}
}
},
"execute": {
"type": "script",
"path": "./scripts/deploy-fleet.sh"
}
}
]
}
#!/bin/bash
scripts/health-check.sh
SERVERS=("51.83.223.88" "152.114.193.86")
for server in "${SERVERS[@]}"; do
echo "=== $server ==="
ssh "$server" "uptime; df -h / | tail -1; free -m | head -2"
done
┌─────────────────────────────────────────────────────────┐
│ S4S Fleet (5 VPS Instances) │
│ │
│ Gateway: 51.83.223.88 (oc.s4s.host, 48c/252GB) │
│ ├── US Instance (port 9377) — Primary │
│ ├── TR Instance (port 9378) — Secondary │
│ ├── PK-C2 (port 9379) — Pakistan │
│ ├── PK-C3 (port 9380) — Pakistan (default) │
│ └── HK-UK2 (152.114.193.86) — Full clone │
│ ├── OpenClaw (port 18789, nginx+SSL) │
│ ├── CamoFox (port 9377) │
│ ├── MariaDB (port 3306) │
│ ├── Hindsight (port 9077) │
│ ├── WA Chat Viewer (port 9376) │
│ └── 12 SSL subdomains (*.hkuk2.s4s.host) │
└─────────────────────────────────────────────────────────┘
#!/bin/bash
fleet-sync.sh — Sync OpenClaw config across fleet
CONFIG_SOURCE="/opt/openclaw/openclaw.json"
TARGETS=("us:51.83.223.88" "tr:51.83.223.88" "pkoc2:51.83.223.88" "hkuk2:152.114.193.86")
echo "🔄 Syncing OpenClaw configuration..."
for target in "${TARGETS[@]}"; do
IFS=':' read -r name host <<< "$target"
echo " → Syncing to $name ($host)..."
rsync -avz "$CONFIG_SOURCE" "$host:/opt/openclaw/openclaw.json"
ssh "$host" "cd /opt/openclaw && docker compose restart openclaw"
echo " ✅ $name synced"
done
echo "🎉 Fleet sync complete"
OpenClaw won't start? ─┬─ Docker not running? ──→ systemctl start docker
├─ Port 18789 in use? ──→ lsof -i :18789 → kill or change port
├─ Config error? ──→ jq . openclaw.json
├─ Redis not running? ──→ docker compose restart redis
├─ Permission denied? ──→ sudo chown -R $USER:$USER /opt/openclaw
└─ Image pull failure? ──→ docker compose pull
WA issues? ─┬─ QR code expired? ──→ Regenerate: delete session, restart bridge
├─ Bridge process dead? ──→ docker compose restart openclaw
├─ Rate limited? ──→ Reduce message frequency, wait 5min
├─ Media not sending? ──→ Check file size (max 16MB WA limit)
└─ Group messages not working? ──→ Check groups.enabled in config
Browser fails? ─┬─ CamoFox port not responding? ──→ Check systemd status, restart
├─ Cloudflare block? ──→ Switch to Patchright (port 9378)
├─ DataDome detection? ──→ Use Patchright + residential proxy
├─ Rate limited? ──→ Add delays between requests (2-5s)
├─ Element not found? ──→ Refresh snapshot, use full=True
└─ SSL error? ──→ Update CA certs: apt install ca-certificates
Provider error? ─┬─ 401 ──→ Check API key in openclaw.json env block
├─ 429 ──→ Rate limited, wait or switch provider
├─ 500 ──→ Provider down, try fallback chain
├─ Timeout ──→ Increase maxTokens or reduce input
└─ Invalid model ──→ Check model name in provider docs
┌──────────────────────────────────────────────────────────────┐
│ Optimization │ Impact │ Complexity │
├──────────────────────────────────────────────────────────────┤
│ Redis for session caching │ -40% latency │ Easy │
│ Nginx SSL termination │ -30% TLS time │ Easy │
│ Docker resource limits │ Stable perf │ Easy │
│ Model response caching │ -60% repeated │ Medium │
│ Skill lazy-loading │ -50% cold start │ Medium │
│ WebSocket connection pool │ +2x throughput │ Medium │
│ Redis queue for messages │ +3x throughput │ Hard │
└──────────────────────────────────────────────────────────────┘
nohup or background &. Orphaned bridge processes break WhatsApp.systemctl restart hermes-gateway with KillMode=control-grouptar czf wa-backup.tar.gz data/whatsapp-session/nohup for bridge | WhatsApp stops responding randomly | Use systemctl restart hermes-gateway |
| Stale WhatsApp session | QR code loop, can't authenticate | Delete session dir, re-authenticate |
| Missing API key in env | Provider returns 401 | Add key to openclaw.json env block |
| Docker volume not mounted | Data lost on restart | Add volume mounts in compose file |
| No nginx SSL termination | Mixed content errors, slow TLS | Configure nginx HTTPS reverse proxy |
| Overloaded context window | Agent loses track mid-conversation | Reduce skills loaded, compress history |
| Wrong browser for site | Bot detection, captcha | Switch CamoFox → Patchright → Scrapling |
| Disk full from media | Agent stops, sessions corrupt | Monitor with cron + df -h alerts |
| Orphaned Docker containers | Port conflicts, zombie processes | docker compose down && docker compose up -d |
| Skills in wrong directory | Skill not discovered | Check openclaw skills list for path |
# Environment variable approach (NEVER hardcode in config.json)
openclaw.json env block references env vars:
"env": {
"CHUTES_API_KEY": "chute_xxxxx",
"OPENROUTER_API_KEY": "sk-or-v1-xxxxx"
}
For production, use Docker secrets or vault:
echo "chute_xxxxx" | docker secret create chutes_api_key -
Rotate keys quarterly
Calendar reminder: Q1=Jan, Q2=Apr, Q3=Jul, Q4=Oct
def validate_openclaw_config(config: dict) -> list[str]:
"""Validate OpenClaw configuration for security issues."""
errors = []
# Check for hardcoded API keys in non-env sections
config_str = json.dumps(config)
api_key_patterns = [
r'sk-[a-zA-Z0-9]{20,}', # OpenAI-style keys
r'cpk_[a-zA-Z0-9]{20,}', # Chutes keys
r'gsk_[a-zA-Z0-9]{20,}', # Groq keys
]
for pattern in api_key_patterns:
import re
if re.search(pattern, config_str):
# Ignore env block references
matches = re.findall(pattern, config_str)
for m in matches:
# Check if it's outside the env block
if '"env"' not in config_str[:config_str.index(m)]:
errors.append(f"Hardcoded API key found: {m[:8]}...")
# Check password strength
auth = config.get("auth", {})
if auth.get("password") and len(auth["password"]) < 8:
errors.append("Auth password too short (min 8 chars)")
# Check for open CORS
# Validate SSL config
# Check rate limits
return errors
# docker-compose.yml security hardening
services:
openclaw:
image: openclaw/openclaw:latest
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /app/data
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:18789/health"]
interval: 30s
timeout: 10s
retries: 3
# Firewall rules (ufw)
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP (for certbot)
ufw allow 443/tcp # HTTPS (nginx)
ufw deny 18789/tcp # Block direct OpenClaw access
ufw deny 9377:9378/tcp # Block direct browser access
ufw deny 3306/tcp # Block direct DB access
ufw deny 6379/tcp # Block direct Redis access
Verify
ufw status numbered
#!/bin/bash
smoke-test.sh — OpenClaw deployment verification
echo "🔍 OpenClaw Smoke Tests"
echo "========================"
Test 1: Gateway health
echo -n "1. Gateway health... "
STATUS=$(curl -sf http://localhost:18789/health | jq -r '.status' 2>/dev/null)
[ "$STATUS" = "ok" ] && echo "✅" || echo "❌ (status: $STATUS)"
Test 2: LLM provider connectivity
echo -n "2. LLM provider... "
RESPONSE=$(curl -sf http://localhost:18789/api/chat \
-H "Content-Type: application/json" \
-d '{"message":"ping","model":"chutes/zai-org/GLM-5-Turbo"}' 2>/dev/null)
[ -n "$RESPONSE" ] && echo "✅" || echo "❌"
Test 3: Skill loading
echo -n "3. Skill count... "
COUNT=$(curl -sf http://localhost:18789/api/skills 2>/dev/null | jq 'length')
[ "$COUNT" -gt 0 ] && echo "✅ ($COUNT skills)" || echo "❌"
Test 4: Redis connectivity
echo -n "4. Redis... "
docker exec openclaw-redis redis-cli ping 2>/dev/null | grep -q PONG && echo "✅" || echo "❌"
Test 5: Nginx SSL
echo -n "5. SSL termination... "
curl -sf https://openclaw.munaf.oc.s4s.host/health >/dev/null 2>&1 && echo "✅" || echo "❌"
Test 6: Browser automation
echo -n "6. CamoFox... "
curl -sf http://localhost:9377/health >/dev/null 2>&1 && echo "✅" || echo "❌"
echo "========================"
echo "Smoke tests complete"
# Basic load test with hey (HTTP load generator)
hey -n 100 -c 10 -m POST \
-H "Content-Type: application/json" \
-d '{"message":"Hello, how are you?","model":"chutes/zai-org/GLM-5-Turbo"}' \
http://localhost:18789/api/chat
Expected results:
- p95 latency < 5s (for simple queries)
- 0% error rate
- Throughput > 10 req/s