Deploy OpenClaw Securely on a VPS

14 min read·Matthieu·OpenClawDockerSecurityNginxsystemdFirewall|

Install and lock down OpenClaw on a VPS with gateway authentication, TLS reverse proxy, Docker sandboxing, firewall hardening, and systemd isolation. Every step includes a verification command.

OpenClaw is an open-source, self-hosted AI assistant that connects to messaging apps (WhatsApp, Telegram, Discord, Slack, Signal) and AI model providers. It runs a Gateway process on your server, receives messages, executes agent actions using tools, and sends responses. All data stays on your infrastructure.

This guide walks through a security-first deployment on a VPS running Ubuntu 24.04. You will harden the OS, install OpenClaw, configure gateway authentication, set up Nginx with TLS as a reverse proxy, fix the Docker/UFW firewall bypass, enable Docker sandboxing, lock down tool policy, and run the gateway as a hardened systemd service.

Why does OpenClaw security matter on a VPS?

OpenClaw runs AI agents that can execute shell commands, read files, and browse the web on your server. A misconfigured instance gives attackers those same capabilities. This is not theoretical.

In early 2026, researchers found over 42,000 OpenClaw instances exposed on the public internet. 63% were vulnerable to remote exploitation. CVE-2026-25253 (CVSS 8.8) allowed one-click remote code execution through auth token exfiltration via a malicious link. The attacker could steal the gateway token, connect via WebSocket, disable confirmation prompts, escape Docker sandboxing, and execute arbitrary commands.

The fix landed in version 2026.1.29, but patching alone is not enough. Security requires layers: network isolation, authentication, TLS, sandboxing, and tool restrictions working together. A single missing layer (say, gateway auth without a firewall) leaves a direct path to compromise.

What do you need before starting?

Requirement Details
VPS Ubuntu 24.04, 4+ vCPU, 8 GB RAM minimum
Domain name A record pointing to your VPS IP (e.g. openclaw.example.com)
SSH access Key-based authentication configured
AI provider API key Anthropic, OpenAI, or Google Gemini
Node.js Version 22 or newer
Docker Engine 20+ with Compose plugin

If you need help provisioning a VPS or setting up SSH keys, see Secure a Linux VPS.

How do you harden the VPS before installing OpenClaw?

Before installing anything, lock down the OS. If you already followed Secure a Linux VPS and SSH Security Configuration, skip to the next section.

Create a dedicated user

Running services as root is unnecessary risk. Create an openclaw system user:

sudo useradd -r -m -s /bin/bash openclaw

This creates a system account with a home directory. OpenClaw stores its configuration in ~/.openclaw, so the home directory is required.

Configure UFW

Install and enable the firewall before exposing any services:

sudo apt update && sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable

Verify the rules:

sudo ufw status verbose

You should see the three allowed ports and a default deny policy for incoming traffic. Port 18789 (the OpenClaw gateway) is intentionally missing. The gateway binds to localhost only and sits behind Nginx.

Install fail2ban

sudo apt update && sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban

Verify it is running:

sudo systemctl status fail2ban

The output should show active (running). fail2ban watches SSH logs and bans IPs after repeated failed login attempts.

How do you install OpenClaw on Ubuntu?

OpenClaw supports two installation methods: npm (direct) and Docker. Both are covered below. Choose one. Docker is recommended for VPS deployments because container isolation limits blast radius if the agent process is compromised.

Install Docker if not already present:

sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Add the openclaw user to the docker group:

sudo usermod -aG docker openclaw

Note: Docker group membership grants the equivalent of root access on the host. This is required for OpenClaw's Docker functionality, but it means the openclaw user can control all containers on the system. The systemd hardening and sandbox isolation below limit what the OpenClaw process can actually do.

Switch to the openclaw user and set up OpenClaw:

sudo -u openclaw -i

Clone the repository and run setup with the official pre-built image:

git clone https://github.com/openclaw/openclaw.git ~/openclaw-src
cd ~/openclaw-src
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest ./docker-setup.sh

Setting OPENCLAW_IMAGE tells the script to pull the pre-built image from GitHub Container Registry instead of building from source. The script runs onboarding and starts the gateway via Docker Compose.

The gateway inside the container binds to all interfaces by default (lan mode). This is correct for Docker deployments because Docker's port mapping needs to reach the gateway inside the container. External access is blocked at the host level using DOCKER-USER iptables rules (covered below).

Verify the gateway is running:

curl -fsS http://127.0.0.1:18789/healthz

A 200 OK response confirms the gateway is live. The /readyz endpoint confirms it is ready to accept connections:

curl -fsS http://127.0.0.1:18789/readyz

Option B: npm installation

Install Node.js 22 using NodeSource:

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

Verify the version:

node --version

The output should show v22.x.x or newer.

Switch to the openclaw user and install:

sudo -u openclaw -i
npm install -g openclaw@latest

Run onboarding:

openclaw onboard

Select loopback for the bind mode. The onboarding wizard generates a gateway auth token and stores it in ~/.openclaw/openclaw.json.

Start the gateway:

openclaw gateway start

Verify:

curl -fsS http://127.0.0.1:18789/healthz

Verify the gateway binding

Check what the gateway is listening on:

ss -tulpn | grep 18789

For npm installations: The output should show 127.0.0.1:18789, not 0.0.0.0:18789. If you see 0.0.0.0, stop the gateway and set gateway.bind to "loopback" in openclaw.json.

For Docker installations: You will see 0.0.0.0:18789 from the docker-proxy process. This is expected. Docker's port mapping publishes the port on all host interfaces so localhost traffic can reach the container. The DOCKER-USER iptables rules (covered in the firewall section below) block external access at the network level.

Lock down file permissions

The configuration file contains your auth token and API keys:

chmod 700 /home/openclaw/.openclaw
chmod 600 /home/openclaw/.openclaw/openclaw.json

Verify:

ls -la /home/openclaw/.openclaw/openclaw.json

You should see -rw-------. For npm installations, the owner is openclaw. For Docker installations, the owner may show as uid 1000 (the node user inside the container). This is normal and required for the container to read the config.

How do you configure gateway authentication?

OpenClaw's gateway requires authentication by default. If no token or password is configured, the gateway refuses WebSocket connections (fail-closed). The onboarding wizard generates a token automatically.

OpenClaw supports three auth modes:

Mode How it works Best for
token Shared bearer token in every request Single-user VPS (recommended)
password Password-based authentication Multi-device setups
trusted-proxy Delegates auth to a reverse proxy Enterprise/SSO setups

Set a strong token

If you want to replace the auto-generated token, generate a new one:

openssl rand -base64 32

Store it as an environment variable instead of hardcoding it in the config file. Create an environment file:

sudo mkdir -p /etc/openclaw
sudo tee /etc/openclaw/env > /dev/null << 'EOF'
OPENCLAW_GATEWAY_TOKEN=your-generated-token-here
EOF
sudo chmod 600 /etc/openclaw/env
sudo chown openclaw:openclaw /etc/openclaw/env

Edit ~/.openclaw/openclaw.json to reference the environment variable:

{
  gateway: {
    bind: "loopback",  // use "loopback" for npm, keep default for Docker
    port: 18789,
    auth: {
      mode: "token",
      token: "${OPENCLAW_GATEWAY_TOKEN}"
    }
  }
}

OpenClaw supports ${VARIABLE} substitution in its JSON5 config, so the token is read from the environment at startup. For Docker installations, set the OPENCLAW_GATEWAY_TOKEN variable in the .env file inside your openclaw-src directory instead. The docker-setup.sh script generates this file automatically.

Restart the gateway and test that unauthenticated requests are rejected:

curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/healthz

The health endpoint returns 200 without auth (it is a health check). To test auth enforcement on the WebSocket endpoint, check the gateway logs:

For systemd/npm installations:

journalctl -u openclaw-gateway --no-pager -n 20

For Docker installations:

cd ~/openclaw-src && docker compose logs openclaw-gateway --tail 20

Look for auth required or connection rejected entries confirming the fail-closed behavior.

How do you set up Nginx as a TLS reverse proxy for OpenClaw?

The gateway binds to localhost. Nginx sits in front, terminates TLS, and proxies WebSocket connections to the gateway. This gives you encrypted connections without exposing the gateway port.

For background on Nginx reverse proxies, see Nginx Reverse Proxy. For TLS setup with Let's Encrypt, see Nginx SSL/TLS with Let's Encrypt.

Install Nginx and Certbot

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

Obtain a TLS certificate

sudo certbot --nginx -d openclaw.example.com

Replace openclaw.example.com with your actual domain. Certbot configures automatic renewal.

Configure the reverse proxy

Create the Nginx server block:

sudo tee /etc/nginx/sites-available/openclaw.conf > /dev/null << 'NGINX'
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name openclaw.example.com;

    ssl_certificate /etc/letsencrypt/live/openclaw.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/openclaw.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Hide Nginx version
    server_tokens off;

    # WebSocket proxy to OpenClaw gateway
    location / {
        proxy_pass http://127.0.0.1:18789;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Overwrite X-Forwarded-For, do not append
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;

        # Timeouts for long-running agent sessions
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name openclaw.example.com;
    return 301 https://$host$request_uri;
}
NGINX

Sharp eyes: notice proxy_set_header X-Forwarded-For $remote_addr uses $remote_addr (overwrite), not $proxy_add_x_forwarded_for (append). This is intentional. Appending lets clients inject fake IPs in the header. The OpenClaw docs specifically recommend overwriting.

Enable the site and test the config:

sudo ln -s /etc/nginx/sites-available/openclaw.conf /etc/nginx/sites-enabled/
sudo nginx -t

The output should show syntax is ok and test is successful. Reload Nginx:

sudo systemctl reload nginx

Configure OpenClaw to trust the proxy

Tell the gateway to trust Nginx's forwarded headers by adding trustedProxies to ~/.openclaw/openclaw.json:

{
  gateway: {
    bind: "loopback",  // use "loopback" for npm, keep default for Docker
    port: 18789,
    trustedProxies: ["127.0.0.1"],
    auth: {
      mode: "token",
      token: "${OPENCLAW_GATEWAY_TOKEN}"
    }
  }
}

Restart the gateway to apply. Verify TLS is working from your local machine (not the server):

curl -I https://openclaw.example.com/healthz

You should get a 200 response over HTTPS. For more Nginx security hardening, see the Nginx security hardening guide (coming soon).

How do you fix the Docker and UFW firewall bypass?

Docker manipulates iptables directly, bypassing UFW entirely. If you run OpenClaw with Docker and publish a port, that port is accessible from the internet even if UFW blocks it. This is one of the most common misconfigurations in Docker deployments.

For a full explanation of this problem, see Docker UFW Firewall Fix.

The fix uses the DOCKER-USER chain, which Docker processes before forwarding traffic to containers.

Block all external access to Docker ports

sudo iptables -I DOCKER-USER -i eth0 -j DROP

This drops all traffic from the external interface (eth0) that Docker would otherwise forward to containers. Adjust the interface name if yours differs (check with ip link show).

Allow loopback traffic

Nginx on the same host needs to reach the gateway container on localhost:

sudo iptables -I DOCKER-USER -i lo -j ACCEPT

Allow established connections

sudo iptables -I DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Make rules persistent

sudo apt install -y iptables-persistent
sudo netfilter-persistent save

Verify the rules

sudo iptables -L DOCKER-USER -n -v

You should see the ACCEPT rules for loopback and established connections above the DROP rule for eth0. Order matters: iptables processes rules top to bottom.

Test from an external machine that the gateway port is not reachable:

nmap -p 18789 your-server-ip

Port 18789 should show filtered or closed, not open.

How do you enable Docker sandboxing for OpenClaw?

Docker sandboxing runs agent tool executions inside isolated containers. When an agent runs a shell command or writes a file, it happens in a disposable container, not on your host.

Add the sandbox configuration to ~/.openclaw/openclaw.json:

{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        scope: "session",
        workspaceAccess: "none",
        docker: {
          image: "openclaw-sandbox:bookworm-slim",
          network: "none",
          user: "1000:1000",
          memory: "1g",
          cpus: 1
        }
      }
    }
  }
}

What each setting does:

Setting Value Effect
mode "all" Every session runs sandboxed, no exceptions
scope "session" Each chat session gets its own container
workspaceAccess "none" Sandbox cannot see the agent workspace
network "none" No network access from sandbox (prevents exfiltration)
memory "1g" Caps container RAM at 1 GB
cpus 1 Caps container to 1 CPU core

Build the sandbox image:

cd ~/openclaw-src
scripts/sandbox-setup.sh

Verify sandbox configuration:

openclaw sandbox explain

For Docker installations, run via compose:

cd ~/openclaw-src && docker compose exec openclaw-gateway node dist/index.js sandbox explain

This command prints the effective sandbox mode, scope, workspace access, tool policy, and any overrides. Confirm mode: all and network: none appear in the output.

If you need network access for specific tools (e.g., web_search), grant it selectively per agent rather than changing the global default. See the tool policy section below.

OpenClaw blocks dangerous bind mount sources by default: /var/run/docker.sock, /etc, /proc, /sys, and /dev are all denied. Do not override this with custom bind mounts that re-expose them. If a sandbox container needs access to host data, use read-only mounts with explicit paths:

{
  agents: {
    defaults: {
      sandbox: {
        docker: {
          binds: ["/home/openclaw/shared-data:/data:ro"]
        }
      }
    }
  }
}

The :ro suffix ensures the container can read the data but not modify it.

How do you lock down OpenClaw tool policy?

Tool policy controls which tools agents can use. The sandbox has its own tool filter separate from agent-level permissions. Deny always wins over allow.

Default deny policy

Set a restrictive default in ~/.openclaw/openclaw.json:

{
  tools: {
    deny: ["exec", "write", "edit", "browser"],
    allow: ["read", "web_search"]
  }
}

This blocks command execution, file writing, file editing, and browser access by default. Agents can still read files and search the web.

Per-agent profiles

Override defaults for specific agents that need more access. Add profiles under agents.list:

{
  agents: {
    list: [
      {
        name: "coding-agent",
        tools: {
          allow: ["exec", "read", "write", "edit"],
          deny: ["browser"]
        },
        sandbox: {
          mode: "all",
          docker: {
            network: "none"
          }
        }
      },
      {
        name: "messaging-agent",
        tools: {
          allow: ["read", "web_search"],
          deny: ["exec", "write", "edit", "browser"]
        }
      }
    ]
  }
}

The coding-agent can execute commands but only inside a sandboxed container with no network. The messaging-agent can read and search but cannot execute anything.

Disable elevated mode

Elevated mode lets agents run commands directly on the gateway host, bypassing the sandbox. Disable it:

{
  tools: {
    elevated: {
      enabled: false
    }
  }
}

If you leave elevated mode enabled, any user who can chat with the agent can potentially run host commands. On a VPS, this is remote code execution.

Multi-user DM isolation

If multiple people will message your OpenClaw instance, enable per-peer DM scoping. By default, all DMs share a single main session. This means User A can see context from User B's conversation.

{
  session: {
    dmScope: "per-peer"
  }
}

This gives each sender their own session with isolated context and sandbox container.

How do you run OpenClaw as a hardened systemd service?

Running OpenClaw under systemd means it starts on boot, restarts on crash, and gets process isolation through hardening directives.

Create the service unit

For Docker installations, create the following unit file:

sudo tee /etc/systemd/system/openclaw-gateway.service > /dev/null << 'EOF'
[Unit]
Description=OpenClaw Gateway
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Type=simple
User=openclaw
Group=openclaw
WorkingDirectory=/home/openclaw/openclaw-src
EnvironmentFile=/etc/openclaw/env
ExecStart=/usr/bin/docker compose up --no-log-prefix openclaw-gateway
ExecStop=/usr/bin/docker compose down
Restart=always
RestartSec=10

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/openclaw/.openclaw
CapabilityBoundingSet=
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
MemoryMax=4G

[Install]
WantedBy=multi-user.target
EOF

For npm installations, replace the ExecStart and ExecStop lines:

ExecStart=/usr/bin/openclaw gateway start --foreground
ExecStop=/usr/bin/openclaw gateway stop

And remove the Requires=docker.service line.

What each hardening directive does:

Directive Protection
NoNewPrivileges Prevents the process from gaining additional privileges via setuid/setgid
PrivateTmp Gives the service its own /tmp, invisible to other processes
ProtectSystem=strict Mounts the entire filesystem read-only except explicit ReadWritePaths
ProtectHome=read-only Prevents writing to any home directory except the allowed path
CapabilityBoundingSet= Drops all Linux capabilities (empty set)
ProtectKernelTunables Blocks writes to /proc/sys, /sys
ProtectKernelModules Prevents loading kernel modules
MemoryMax=4G Kills the service if it exceeds 4 GB RAM, preventing OOM on the host

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now openclaw-gateway

enable makes it start on boot. --now starts it immediately.

Verify it is running:

sudo systemctl status openclaw-gateway

The output should show active (running). Check the logs for any errors:

journalctl -u openclaw-gateway -f --no-pager -n 50

How do you verify the full deployment is secure?

Run through this checklist after deployment to confirm every layer is working.

1. Run OpenClaw's built-in security audit

For npm installations:

sudo -u openclaw openclaw security audit --deep

For Docker installations:

cd ~/openclaw-src && docker compose exec openclaw-gateway node dist/index.js security audit --deep

This flags common misconfigurations: unauthenticated network exposure, elevated tool permissions, file permission issues. Fix any warnings before proceeding.

2. Confirm gateway binding

ss -tulpn | grep 18789

npm installations: Expected 127.0.0.1:18789. Not 0.0.0.0:18789.

Docker installations: Expected 0.0.0.0:18789 from docker-proxy. This is normal. External access is blocked by the DOCKER-USER iptables rules.

3. External port scan

From your local machine (not the server):

nmap -p 18789 your-server-ip

Expected: filtered or closed.

4. Verify TLS

curl -I https://openclaw.example.com/healthz

Expected: 200 over HTTPS with a valid certificate.

5. Test auth enforcement

curl -s -o /dev/null -w "%{http_code}" https://openclaw.example.com/

The gateway should require authentication for non-health endpoints.

6. Check sandbox isolation

For npm installations:

sudo -u openclaw openclaw sandbox explain

For Docker installations:

cd ~/openclaw-src && docker compose exec openclaw-gateway node dist/index.js sandbox explain

Confirm mode: all, network: none, and no elevated overrides.

7. Verify file permissions

ls -la /home/openclaw/.openclaw/openclaw.json
ls -la /etc/openclaw/env

Both should show 600 permissions. The env file is owned by openclaw. For Docker installations, openclaw.json may be owned by uid 1000 (the container's node user).

8. Check systemd hardening

systemd-analyze security openclaw-gateway

This scores the service's security properties. Aim for a score around 5.0 or lower (lower is more secure). Docker-based services typically score around 5.0-5.5 because Docker requires access to namespaces and device interfaces. The hardening directives above bring the score down from the default ~9.6 to roughly 5.2.

9. Verify UFW and DOCKER-USER rules

sudo ufw status
sudo iptables -L DOCKER-USER -n -v

UFW should show only ports 22, 80, and 443. The DOCKER-USER chain should drop external traffic.

10. Check for exposed services

ss -tulpn

Only SSH (22), Nginx (80, 443), and OpenClaw (127.0.0.1:18789 for npm, or 0.0.0.0:18789 via docker-proxy for Docker) should appear.

How do you keep OpenClaw updated and backed up?

Version hiding

Hide the OpenClaw version in gateway responses. Version disclosure helps attackers target known vulnerabilities. In ~/.openclaw/openclaw.json:

{
  gateway: {
    exposeVersion: false
  }
}

Combined with the server_tokens off; directive already in the Nginx config, this prevents fingerprinting both the reverse proxy and the application.

Updates

OpenClaw releases frequently. Check the current version:

openclaw --version

For Docker installations:

cd ~/openclaw-src && docker compose exec openclaw-gateway node dist/index.js --version

To update a Docker installation:

cd ~/openclaw-src
git pull
docker compose pull
docker compose up -d openclaw-gateway

To update an npm installation:

npm update -g openclaw

After updating, restart the gateway and re-run the security audit:

sudo systemctl restart openclaw-gateway

For npm:

sudo -u openclaw openclaw security audit

For Docker:

cd ~/openclaw-src && docker compose exec openclaw-gateway node dist/index.js security audit

Read the release notes before upgrading. Breaking changes in config format do happen. Version 2026.3.7 introduced a required gateway.auth.mode field when both token and password are present. Missing it after the upgrade locks you out of the gateway.

Backups

Back up the configuration directory:

sudo tar czf /root/openclaw-backup-$(date +%Y%m%d).tar.gz /home/openclaw/.openclaw

Store backups off-server. The config directory contains your auth tokens, agent configurations, and conversation state. Protect backups with the same care as the live config: encrypt them with gpg or store them in an encrypted volume.

Log management

View logs in real time:

journalctl -u openclaw-gateway -f

OpenClaw logs may contain conversation snippets. Set log retention to limit exposure:

sudo tee /etc/systemd/journald.conf.d/openclaw.conf > /dev/null << 'EOF'
[Journal]
MaxRetentionSec=7d
MaxFileSec=1d
EOF
sudo systemctl restart systemd-journald

Something went wrong?

Symptom Likely cause Fix
Gateway refuses to start Port 18789 already in use ss -tulpn | grep 18789 to find the conflicting process
WebSocket connections fail through Nginx Missing Upgrade and Connection headers Check the proxy_set_header directives in your Nginx config
Sandbox containers fail to start Docker socket permissions Ensure the openclaw user is in the docker group and re-login
openclaw security audit shows warnings Config drift after update Review the audit output and apply recommended fixes
Gateway accessible from external IP Docker/UFW bypass Add DOCKER-USER iptables rules as described above
TLS certificate errors Certbot renewal failed Run sudo certbot renew --dry-run to diagnose
OOM kills on the VPS Missing MemoryMax in systemd unit Add MemoryMax=4G to the [Service] section

Copyright 2026 Virtua.Cloud. All rights reserved. This content is original work by the Virtua.Cloud team. Reproduction, republication, or redistribution without written permission is prohibited.

Ready to try it yourself?

Deploy your own server in seconds. Linux, Windows, or FreeBSD.

See VPS Plans