OpenClaw on Your VPS Series
    Part 2 of 6

    Installation and Security Hardening

    From a fresh Ubuntu VPS to a running, hardened OpenClaw Gateway with Nginx SSL and systemd auto-restart.

    35 minutes
    Security-focused

    ⚠️ Security Warning

    Do not skip the security steps. OpenClaw binds to 0.0.0.0 by default, which exposes the control API to the public internet on port 18789. In early 2026, Censys found over 21,000 exposed instances before CVE-2026-25253 (a one-click RCE via WebSocket hijacking) was patched. The fix is in current versions, but the exposure problem is architectural and yours to solve.

    1Initial Server Configuration

    Create a dedicated service user. Running the Gateway as root is unnecessary and significantly increases blast radius if a skill misbehaves.

    Create non-root user
    # Update the system
    apt update && apt upgrade -y
    
    # Create a non-root user for OpenClaw
    adduser openclawops
    usermod -aG sudo openclawops
    
    # Copy your SSH authorized_keys to the new user
    mkdir -p /home/openclawops/.ssh
    cp ~/.ssh/authorized_keys /home/openclawops/.ssh/
    chown -R openclawops:openclawops /home/openclawops/.ssh
    chmod 700 /home/openclawops/.ssh
    chmod 600 /home/openclawops/.ssh/authorized_keys

    Disconnect and reconnect as openclawops before continuing. Do not run the rest of this guide as root.

    2Firewall Configuration with UFW

    UFW setup
    # Set default policies
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    
    # Allow SSH with rate limiting
    sudo ufw allow 22/tcp
    sudo ufw limit 22/tcp
    
    # Allow HTTP and HTTPS for Nginx
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    
    # Explicitly block the OpenClaw Gateway port from public access
    sudo ufw deny 18789/tcp
    
    # Enable the firewall
    sudo ufw enable
    
    # Verify rules
    sudo ufw status verbose

    The Gateway will be accessible through Nginx on port 443 after SSL is configured. Port 18789 stays closed to the public.

    3Install fail2ban

    fail2ban monitors log files and bans IPs that show signs of brute-force activity. On a public VPS this is not optional.

    Install and configure
    sudo apt install fail2ban -y
    
    # Create a local config that survives package updates
    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

    Edit the [sshd] section in /etc/fail2ban/jail.local:

    /etc/fail2ban/jail.local
    [sshd]
    enabled = true
    port    = ssh
    filter  = sshd
    logpath = %(sshd_log)s
    maxretry = 5
    bantime = 3600
    findtime = 600
    sudo systemctl enable fail2ban
    sudo systemctl start fail2ban

    4Install Node.js 24

    The official OpenClaw documentation requires Node.js 22.16 at minimum and recommends Node.js 24. Older versions produce silent failures.

    Install from NodeSource
    # Install NodeSource repository for Node.js 24
    curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
    
    sudo apt install nodejs -y
    
    # Verify versions
    node --version   # Should output v24.x.x
    npm --version

    5Install OpenClaw (Native Install)

    The recommended first-install path uses the official onboard script with the --install-daemon flag to register a systemd service.

    Install and onboard
    # Install OpenClaw globally
    npm install -g openclaw@latest
    
    # Verify the install
    openclaw --version
    
    # Run the interactive onboard wizard
    openclaw onboard --install-daemon

    The wizard will configure your model provider, workspace directory, gateway settings, and optional channels.

    🔒 Critical Security Configuration

    When asked about the gateway bind address, set it to loopback rather than the default. This restricts the Gateway to localhost only:

    gateway.bind: "loopback"

    If you miss this during onboard:

    openclaw config set gateway.bind loopback
    openclaw gateway restart

    5Alternative: Docker-Based Install

    If you prefer container isolation, Docker is a supported install path.

    Docker install
    # Install Docker
    curl -fsSL https://get.docker.com | sudo bash
    sudo usermod -aG docker openclawops
    
    # Log out and back in, then:
    mkdir -p ~/openclaw
    cd ~/openclaw
    
    cat > docker-compose.yml <<'EOF'
    services:
      openclaw:
        image: openclaw/openclaw:latest
        container_name: openclaw
        restart: unless-stopped
        ports:
          - "127.0.0.1:18789:18789"
        volumes:
          - openclaw_config:/root/.openclaw
          - openclaw_workspace:/root/.openclaw/workspace
        environment:
          - OPENCLAW_GATEWAY_BIND=loopback
    
    volumes:
      openclaw_config:
      openclaw_workspace:
    EOF
    
    docker compose up -d
    docker compose logs -f openclaw

    The 127.0.0.1:18789:18789 port binding exposes the Gateway only on the loopback interface — not on the public IP.

    6Verify the systemd Service (Native Install)

    Check service status
    # Check service status
    systemctl --user status openclaw
    
    # Enable at boot
    systemctl --user enable openclaw
    
    # View logs
    journalctl --user -u openclaw -f
    
    # Enable lingering so service starts without login
    sudo loginctl enable-linger openclawops

    7Nginx Reverse Proxy with SSL

    Nginx terminates SSL and proxies to 127.0.0.1:18789. Port 18789 never needs to be open to the public.

    sudo apt install nginx certbot python3-certbot-nginx -y

    Create /etc/nginx/sites-available/openclaw:

    Nginx config
    server {
        listen 80;
        server_name agent.yourdomain.com;
    
        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;
        }
    }

    The Upgrade and Connection headers are required — OpenClaw uses WebSockets for real-time communication.

    Enable and get SSL
    # Enable the site
    sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
    
    # Test and reload Nginx
    sudo nginx -t
    sudo systemctl reload nginx
    
    # Obtain SSL certificate
    sudo certbot --nginx -d agent.yourdomain.com
    
    # Verify auto-renewal
    sudo certbot renew --dry-run

    8Set a Gateway Auth Token

    Even behind Nginx with SSL, the Control UI should require authentication.

    Generate and set token
    # Generate a strong token
    openssl rand -hex 32
    
    # Set it in OpenClaw
    openclaw config set gateway.token "your-strong-random-token-here"
    openclaw gateway restart

    9Harden Credential Storage

    OpenClaw stores API keys and credentials in plaintext under ~/.openclaw/credentials/. This is a known target for infostealers.

    Lock down permissions
    chmod 700 ~/.openclaw/credentials/
    chmod 600 ~/.openclaw/credentials/*
    
    # Also restrict the main config directory
    chmod 700 ~/.openclaw/

    10Verify the Deployment

    openclaw doctor

    This checks runtime status, channel connections, Gateway reachability, and common misconfigurations. Then open https://agent.yourdomain.com and send a test message.

    Key File Locations

    PathPurpose
    ~/.openclaw/Main config directory
    ~/.openclaw/credentials/API keys and channel secrets
    ~/.openclaw/workspace/Agent memory, skills, notes
    ~/.openclaw/openclaw.config.yamlMain configuration file
    /etc/nginx/sites-available/openclawNginx server block