Deploy OpenClaw on Private Cloud in 45 Minutes (Security-Hardened)

Run the OpenClaw AI agent on your own infrastructure with Docker isolation, secret management, and network hardening to prevent credential theft.

Problem: OpenClaw's Default Setup Exposes Your Credentials

You want to run OpenClaw for AI automation, but the default installation stores API keys in plaintext and runs with excessive privileges — creating a direct path to credential theft and system compromise.

You'll learn:

  • Deploy OpenClaw in an isolated Docker environment
  • Implement proper secret management (no plaintext keys)
  • Harden network access and container permissions
  • Monitor for security violations in real-time

Time: 45 min | Level: Advanced


Why This Matters (The Security Context)

In January 2026, security researchers discovered OpenClaw stores credentials in plaintext configuration files, making them trivial to steal. Over 42,000 exposed instances were found publicly accessible, and CVE-2026-25253 allowed one-click remote code execution via token exfiltration.

Common attack vectors:

  • Plaintext API keys in ~/.openclaw/config.json
  • Prompt injection through email/web content
  • Malicious skills from ClawHub marketplace (7% contain credential leaks)
  • Cross-site WebSocket hijacking (patched in v2026.1.29+)

Why private cloud deployment helps: Running on your own infrastructure (not your laptop) isolates the attack surface. Combined with proper hardening, you get AI automation without risking your primary systems.


Prerequisites

Required:

  • Linux server (Ubuntu 24.04+ recommended) or VPS
  • Docker Engine 24.0+ with Docker Compose v2
  • 2GB RAM minimum ($24/month VPS tier)
  • Domain name (optional, for TLS)

Security tools needed:

# Install Docker if not present
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Verify versions
docker --version  # Should be 24.0+
docker compose version  # Should be v2.x

Anthropic API key: Get one at https://console.anthropic.com/settings/keys (OpenClaw works best with Claude Sonnet 4.5)


Solution: Security-Hardened Deployment

Step 1: Clone and Prepare Environment

# Clone OpenClaw repository
git clone https://github.com/openclaw/openclaw.git
cd openclaw

# Checkout latest stable version (avoid bleeding edge)
git checkout tags/v2026.1.30

# Create isolated workspace
mkdir -p ~/openclaw-workspace
chmod 700 ~/openclaw-workspace  # Restrict permissions

Why this works: Using tagged releases avoids unstable main branch builds. The 700 permission ensures only your user can access the workspace.

Expected: You should see openclaw/ directory with Dockerfile and docker-compose.yml


Step 2: Configure Secret Management

DO NOT store API keys in config files. Use environment variables instead.

# Create secrets file (never commit this)
cat > .env.secrets << 'EOF'
# Anthropic API Key
ANTHROPIC_API_KEY=sk-ant-your-key-here

# Gateway authentication token (generate random)
OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)

# Disable localhost trust (critical)
LOCALHOST_TRUST_ENABLED=false
GATEWAY_AUTH_MODE=token
EOF

# Secure the secrets file
chmod 600 .env.secrets

Security note: The LOCALHOST_TRUST_ENABLED=false prevents the CVE-2026-25253 vulnerability. Always generate a unique gateway token.


Step 3: Create Hardened Docker Compose Configuration

Create docker-compose.hardened.yml:

version: '3.8'

services:
  openclaw-gateway:
    image: openclaw/gateway:v2026.1.30
    container_name: openclaw-gateway
    
    # Security hardening
    security_opt:
      - no-new-privileges:true  # Prevent privilege escalation
    cap_drop:
      - ALL  # Drop all capabilities
    cap_add:
      - NET_BIND_SERVICE  # Only allow binding to ports
    
    # Run as non-root user
    user: "1000:1000"
    
    # Read-only root filesystem
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=100M
    
    # Environment from secrets file
    env_file:
      - .env.secrets
    environment:
      - NODE_ENV=production
      - GATEWAY_AUTH_MODE=token
      - LOCALHOST_TRUST_ENABLED=false
    
    # Volume mounts (minimal)
    volumes:
      - openclaw-config:/home/openclaw/.openclaw:rw
      - ~/openclaw-workspace:/workspace:rw
    
    # Network isolation
    networks:
      - openclaw-internal
    
    # Resource limits
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          memory: 512M
    
    # Health check
    healthcheck:
      test: ["CMD", "node", "-e", "process.exit(0)"]
      interval: 30s
      timeout: 10s
      retries: 3
    
    restart: unless-stopped

  # Reverse proxy with TLS (optional but recommended)
  nginx:
    image: nginx:alpine
    container_name: openclaw-nginx
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro  # If using certbot
    networks:
      - openclaw-internal
    depends_on:
      - openclaw-gateway
    restart: unless-stopped

networks:
  openclaw-internal:
    driver: bridge
    internal: true  # No external access except through nginx

volumes:
  openclaw-config:
    driver: local

What this does:

  • Runs container as non-root user (UID 1000)
  • Drops all Linux capabilities except network binding
  • Read-only filesystem prevents malicious writes
  • Internal network isolates from internet
  • Resource limits prevent DoS attacks

Step 4: Configure Nginx Reverse Proxy (Optional TLS)

Create nginx.conf:

events {
    worker_connections 1024;
}

http {
    # Rate limiting to prevent abuse
    limit_req_zone $binary_remote_addr zone=openclaw:10m rate=10r/s;
    
    upstream openclaw {
        server openclaw-gateway:18789;
    }
    
    server {
        listen 443 ssl http2;
        server_name your-domain.com;  # Replace with your domain
        
        # TLS configuration (use certbot to generate certs)
        ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
        
        # Modern TLS only
        ssl_protocols TLSv1.3;
        ssl_prefer_server_ciphers off;
        
        # Security headers
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header X-Frame-Options "DENY" always;
        add_header X-Content-Type-Options "nosniff" always;
        
        # WebSocket support for OpenClaw gateway
        location / {
            limit_req zone=openclaw burst=20 nodelay;
            
            proxy_pass http://openclaw;
            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;
            
            # Prevent token exfiltration (CVE-2026-25253 mitigation)
            proxy_set_header Origin "";
        }
    }
}

If not using TLS: Remove the nginx service and expose port 18789 directly (not recommended for production).


Step 5: Firewall Configuration

# Allow only SSH and HTTPS
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 443/tcp   # HTTPS (if using nginx)
sudo ufw enable

# Verify rules
sudo ufw status verbose

Expected output:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

If it fails:

  • Error: "Command not found": Install ufw with sudo apt install ufw
  • SSH locked out: Add your IP specifically: sudo ufw allow from YOUR_IP to any port 22

Step 6: Launch OpenClaw Gateway

# Start services
docker compose -f docker-compose.hardened.yml up -d

# Check logs for errors
docker compose logs -f openclaw-gateway

# You should see: "Gateway started on port 18789"

Get dashboard access:

docker compose exec openclaw-gateway node cli.js dashboard --no-open

Expected: You'll see a URL with a token like:

http://127.0.0.1:18789/?token=abc123...

Access this via your domain if using nginx: https://your-domain.com/?token=abc123...


Step 7: Pair Messaging Platforms (Telegram Example)

OpenClaw integrates with WhatsApp, Telegram, Slack, and others. Telegram is most secure for private cloud.

# Enter the container
docker compose exec openclaw-gateway sh

# Run onboarding
node cli.js onboard

# Select: Telegram
# Paste your Telegram bot token from @BotFather
# Complete device pairing

Security tip: Create a dedicated Telegram bot for OpenClaw — never use your personal account credentials.


Verification

Test 1: Confirm No Plaintext Secrets

# Check that API keys are NOT in config files
docker compose exec openclaw-gateway sh -c "grep -r 'sk-ant' /home/openclaw/.openclaw/ || echo 'No plaintext keys found'"

You should see: No plaintext keys found

Test 2: Verify Container Isolation

# Attempt to write to root filesystem (should fail)
docker compose exec openclaw-gateway sh -c "touch /test.txt 2>&1 || echo 'Filesystem is read-only (correct)'"

Expected: Filesystem is read-only (correct)

Test 3: Test Gateway Authentication

# Try connecting without token (should fail)
curl https://your-domain.com/api/health

# Expected: 401 Unauthorized or connection refused

Production Checklist

Before using OpenClaw with real credentials:

  • Secrets stored in environment variables (NOT config files)
  • Container runs as non-root user
  • Firewall allows only necessary ports (22, 443)
  • TLS enabled with valid certificate
  • LOCALHOST_TRUST_ENABLED=false in environment
  • Gateway token is randomly generated (32+ chars)
  • OpenClaw version is 2026.1.29 or newer (CVE patched)
  • Read-only filesystem enabled
  • Resource limits configured
  • No ClawHub skills installed (vet thoroughly first)

What You Learned

  • Private cloud deployment isolates OpenClaw from your primary systems
  • Docker hardening (non-root, read-only, dropped caps) prevents container breakout
  • Secret management via environment variables eliminates plaintext credential theft
  • Network isolation + reverse proxy creates defense in depth

Critical limitation: OpenClaw's security model is still maturing. Even hardened deployments can be vulnerable to:

  • Prompt injection via email/web content
  • Malicious ClawHub skills (26% analyzed in late 2025 had vulnerabilities)
  • LLM hallucinations causing unintended actions

Do NOT use for:

  • Production systems with critical data access
  • Corporate environments without explicit security approval
  • Machines with sensitive credentials already stored

Advanced: Monitoring for Security Violations

Add monitoring to detect suspicious activity:

# Add to docker-compose.hardened.yml
  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    networks:
      - openclaw-internal

Create promtail-config.yml:

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: openclaw
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/(openclaw-gateway)'
        action: keep
    
    # Alert on suspicious patterns
    pipeline_stages:
      - match:
          selector: '{job="openclaw"}'
          stages:
            - regex:
                expression: '(?i)(sk-ant|sk-|password|token|secret)'
            - labels:
                alert: credential_leak

What this monitors: Any log output containing API keys or secrets triggers an alert, indicating potential credential exfiltration.


Troubleshooting

"Gateway disconnected (1008): pairing required"

Cause: Gateway token expired or browser not authenticated.

Fix:

docker compose exec openclaw-gateway node cli.js dashboard --no-open
# Get new URL with fresh token

Container exits with "Permission denied"

Cause: Volume mounts have wrong ownership.

Fix:

# Set correct ownership (assuming UID 1000)
sudo chown -R 1000:1000 ~/openclaw-workspace
docker compose -f docker-compose.hardened.yml restart

"Cannot connect to Anthropic API"

Cause: Network isolation preventing outbound connections.

Fix: Modify docker-compose.hardened.yml network:

networks:
  openclaw-internal:
    driver: bridge
    internal: false  # Allow outbound (but still isolated from host)

Then restart:

docker compose -f docker-compose.hardened.yml down
docker compose -f docker-compose.hardened.yml up -d

Cost Breakdown

VPS hosting (minimum specs):

  • DigitalOcean Droplet: $24/month (2GB RAM, 1 CPU)
  • Hetzner Cloud: ~$5/month (2GB RAM equivalent in EU)
  • Linode: $12/month (2GB shared CPU)

API costs:

  • Anthropic Claude Sonnet 4.5: ~$3/million input tokens
  • Typical usage: $10-50/month for personal automation

Total: $30-75/month for production-ready private AI agent


Tested on Ubuntu 24.04, Docker 25.0.1, OpenClaw v2026.1.30

Security disclaimer: This guide implements current best practices as of February 2026. OpenClaw is rapidly evolving software with ongoing security discoveries. Always review the latest security advisories at https://docs.openclaw.ai/security/advisories before deploying to production.

Report vulnerabilities: If you discover security issues, follow responsible disclosure at https://github.com/openclaw/openclaw/security/policy