🔷Cloud Platform

Self-Host OpenClaw on Azure

Deploy OpenClaw on Azure with a hardened Linux VM, private networking, and Azure Bastion for secure SSH access. Step-by-step guide with cost optimization tips.

Difficulty: intermediateTime: ~25 minCost: ~$55/mo

Self-Host OpenClaw on Azure

This guide walks you through deploying OpenClaw on an Azure Linux VM with proper network isolation, security hardening, and Azure Bastion for SSH access. You will end up with a production-ready setup that keeps your AI coding agent behind private networking with no public IP exposure.

Quick Path

For experienced Azure users who know the CLI well:

  1. az login and register Microsoft.Compute, Microsoft.Network, Microsoft.ResourceHealth providers
  2. Create a resource group in your preferred region
  3. Create an NSG with rules: deny all inbound SSH (priority 100), allow AzureBastionSubnet SSH (110), allow outbound HTTPS (120)
  4. Create a VNet with a default subnet and an AzureBastionSubnet
  5. Deploy an Ubuntu 24.04 Standard_B2s VM with no public IP, attached to the NSG
  6. Deploy Azure Bastion (Basic SKU) into the Bastion subnet
  7. SSH through Bastion, run the OpenClaw installer script
  8. Configure and start OpenClaw

Prerequisites

Before starting, make sure you have:

Verify your CLI installation:

az version

Step 1: Authenticate and Configure Azure CLI

Sign in to your Azure account:

az login

If you have multiple subscriptions, set the one you want to use:

az account set --subscription "Your Subscription Name"

Register the required resource providers. These may already be registered, but running the commands is idempotent:

az provider register --namespace Microsoft.Compute
az provider register --namespace Microsoft.Network
az provider register --namespace Microsoft.ResourceHealth

Check registration status (wait until all show Registered):

az provider show --namespace Microsoft.Compute --query "registrationState"
az provider show --namespace Microsoft.Network --query "registrationState"

Step 2: Create a Resource Group

All resources will live in a single resource group for easy management and cleanup:

az group create \
  --name openclaw-rg \
  --location eastus

Choose a region close to your primary working location. eastus and westeurope generally offer the best pricing.

Step 3: Create the Network Security Group

The NSG controls traffic to and from your VM. The strategy here is defense in depth: deny all SSH by default, then allow it only from the Azure Bastion subnet.

az network nsg create \
  --resource-group openclaw-rg \
  --name openclaw-nsg

Add the security rules in priority order:

# Rule 100: Deny all inbound SSH by default
az network nsg rule create \
  --resource-group openclaw-rg \
  --nsg-name openclaw-nsg \
  --name DenyAllSSH \
  --priority 100 \
  --direction Inbound \
  --access Deny \
  --protocol Tcp \
  --destination-port-ranges 22 \
  --source-address-prefixes "*" \
  --destination-address-prefixes "*"

# Rule 110: Allow SSH only from AzureBastionSubnet
az network nsg rule create \
  --resource-group openclaw-rg \
  --nsg-name openclaw-nsg \
  --name AllowBastionSSH \
  --priority 110 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --destination-port-ranges 22 \
  --source-address-prefixes "10.0.1.0/26" \
  --destination-address-prefixes "VirtualNetwork"

# Rule 120: Allow outbound HTTPS (for pulling packages and OpenClaw updates)
az network nsg rule create \
  --resource-group openclaw-rg \
  --nsg-name openclaw-nsg \
  --name AllowOutboundHTTPS \
  --priority 120 \
  --direction Outbound \
  --access Allow \
  --protocol Tcp \
  --destination-port-ranges 443 \
  --source-address-prefixes "VirtualNetwork" \
  --destination-address-prefixes "Internet"

Because lower priority numbers are evaluated first, the Bastion allow rule (110) takes precedence over the deny-all rule for Bastion traffic, while all other SSH attempts are blocked at priority 100.

Step 4: Create the Virtual Network

Create a VNet with two subnets: one for the VM and one for Azure Bastion. The Bastion subnet must be named exactly AzureBastionSubnet and needs at least a /26 CIDR block.

# Create VNet with the VM subnet
az network vnet create \
  --resource-group openclaw-rg \
  --name openclaw-vnet \
  --address-prefix 10.0.0.0/16 \
  --subnet-name default \
  --subnet-prefix 10.0.0.0/24

# Add the Bastion subnet
az network vnet subnet create \
  --resource-group openclaw-rg \
  --vnet-name openclaw-vnet \
  --name AzureBastionSubnet \
  --address-prefix 10.0.1.0/26

# Associate the NSG with the VM subnet
az network vnet subnet update \
  --resource-group openclaw-rg \
  --vnet-name openclaw-vnet \
  --name default \
  --network-security-group openclaw-nsg

Step 5: Create the Virtual Machine

Deploy an Ubuntu 24.04 LTS VM without a public IP address. The Standard_B2s size provides 2 vCPUs and 4 GB of RAM, which is sufficient for most OpenClaw workloads.

az vm create \
  --resource-group openclaw-rg \
  --name openclaw-vm \
  --image Canonical:ubuntu-24_04-lts:server:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --vnet-name openclaw-vnet \
  --subnet default \
  --nsg openclaw-nsg \
  --public-ip-address "" \
  --os-disk-size-gb 30 \
  --storage-sku Premium_LRS

The --public-ip-address "" flag ensures the VM has no public IP. All SSH access goes through Bastion.

Verify the VM is running:

az vm show \
  --resource-group openclaw-rg \
  --name openclaw-vm \
  --show-details \
  --query "{name:name, powerState:powerState, privateIp:privateIps}" \
  --output table

Step 6: Deploy Azure Bastion

Azure Bastion provides managed, browser-based SSH access to your VM without exposing any public endpoints.

First, create a public IP for the Bastion host itself:

az network public-ip create \
  --resource-group openclaw-rg \
  --name openclaw-bastion-ip \
  --sku Standard \
  --allocation-method Static

Then deploy Bastion:

az network bastion create \
  --resource-group openclaw-rg \
  --name openclaw-bastion \
  --public-ip-address openclaw-bastion-ip \
  --vnet-name openclaw-vnet \
  --sku Basic \
  --location eastus

This takes 5-10 minutes to provision. Once complete, you can connect through the Azure Portal or via the CLI:

az network bastion ssh \
  --resource-group openclaw-rg \
  --name openclaw-bastion \
  --target-resource-id $(az vm show --resource-group openclaw-rg --name openclaw-vm --query id -o tsv) \
  --auth-type ssh-key \
  --username azureuser \
  --ssh-key ~/.ssh/id_rsa

Step 7: Install OpenClaw

Once connected to the VM via Bastion, run the OpenClaw installer:

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install OpenClaw using the official installer
curl -fsSL https://get.openclaw.dev | bash

After installation completes, configure OpenClaw with your API key:

# Set your Anthropic API key
export ANTHROPIC_API_KEY="sk-ant-..."

# Start the OpenClaw server
openclaw server start --host 0.0.0.0 --port 18789

For persistent configuration, create the config file:

mkdir -p ~/.openclaw
cat > ~/.openclaw/config.json << 'EOF'
{
  "gateway": {
    "host": "0.0.0.0",
    "port": 18789
  },
  "auth": {
    "token": "your-secure-gateway-token"
  }
}
EOF

Enable OpenClaw as a systemd service so it starts on boot:

sudo tee /etc/systemd/system/openclaw.service > /dev/null << 'EOF'
[Unit]
Description=OpenClaw AI Agent
After=network.target

[Service]
Type=simple
User=azureuser
Environment=ANTHROPIC_API_KEY=sk-ant-...
Environment=OPENCLAW_GATEWAY_TOKEN=your-secure-gateway-token
ExecStart=/usr/local/bin/openclaw server start
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable openclaw
sudo systemctl start openclaw

Step 8: Access OpenClaw

Since the VM has no public IP, you access OpenClaw through an SSH tunnel via Bastion. The tunnel forwards the OpenClaw port to your local machine:

az network bastion tunnel \
  --resource-group openclaw-rg \
  --name openclaw-bastion \
  --target-resource-id $(az vm show --resource-group openclaw-rg --name openclaw-vm --query id -o tsv) \
  --resource-port 18789 \
  --port 18789

With the tunnel active, open http://localhost:18789 in your browser.

Cost Breakdown

ResourceMonthly CostNotes
Standard_B2s VM~$332 vCPU, 4 GB RAM, burstable
Premium SSD (30 GB)~$5P4 managed disk
Azure Bastion (Basic)~$140Billed per hour while deployed
VNet / NSGFreeNo charge for basic networking
Total (Bastion always on)~$178
Total (Bastion on-demand)~$55Delete Bastion when not using SSH

Cost Optimization

Azure Bastion is the largest cost driver. Since you only need it for SSH sessions, the recommended approach is to delete it when idle and recreate it when needed:

# Delete Bastion when done with SSH (saves ~$140/mo)
az network bastion delete \
  --resource-group openclaw-rg \
  --name openclaw-bastion

# Delete the Bastion public IP too
az network public-ip delete \
  --resource-group openclaw-rg \
  --name openclaw-bastion-ip

To recreate it when you need SSH access, re-run the commands from Step 6.

Additionally, deallocate the VM when not in use to stop compute billing:

# Stop and deallocate (no compute charges, disk charges still apply)
az vm deallocate --resource-group openclaw-rg --name openclaw-vm

# Start it back up
az vm start --resource-group openclaw-rg --name openclaw-vm

You can also set up auto-shutdown to deallocate the VM on a schedule:

az vm auto-shutdown \
  --resource-group openclaw-rg \
  --name openclaw-vm \
  --time 2200 \
  --timezone "America/New_York"

Security Best Practices

Troubleshooting

Bastion connection times out

Verify the AzureBastionSubnet exists with at least a /26 prefix and that the Bastion resource is in the Succeeded provisioning state:

az network bastion show \
  --resource-group openclaw-rg \
  --name openclaw-bastion \
  --query provisioningState

VM cannot pull packages

Check that the outbound HTTPS NSG rule is in place and that DNS resolution works inside the VM. Azure VMs use 168.63.129.16 as the default DNS resolver. If apt update fails, verify:

nslookup archive.ubuntu.com
curl -I https://archive.ubuntu.com

OpenClaw fails to start

Check the systemd service logs:

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

Common causes: missing ANTHROPIC_API_KEY, port 18789 already in use, or insufficient memory. Monitor memory usage with free -h.

Cleanup

To remove all resources and stop all billing:

az group delete --name openclaw-rg --yes --no-wait

This deletes the resource group and everything inside it: the VM, disks, VNet, NSG, Bastion, and public IP. The --no-wait flag returns immediately while deletion proceeds in the background.

Frequently Asked Questions

How much does it cost to run OpenClaw on Azure?

The VM itself costs approximately $55/month for a Standard_B2s instance. Azure Bastion adds ~$140/month while running, but you can delete it when not needed and recreate it only for SSH sessions, bringing effective costs much lower.

Can I use a regular public IP instead of Azure Bastion?

Yes, but it is not recommended. Azure Bastion provides a managed jump host that eliminates the need to expose SSH directly to the internet. If you must use a public IP, restrict NSG rules to your specific IP address and enable just-in-time VM access through Microsoft Defender for Cloud.

What Azure VM size should I choose for OpenClaw?

Standard_B2s (2 vCPUs, 4 GB RAM) handles most workloads well. For heavier usage with multiple concurrent sessions, consider Standard_B2ms (2 vCPUs, 8 GB RAM) at roughly $60/month. Avoid A-series VMs as they lack the burst performance OpenClaw benefits from.

How do I keep OpenClaw updated on Azure?

SSH into the VM through Bastion, navigate to the OpenClaw directory, run git pull to fetch the latest changes, then restart the service. You can also automate this with Azure Automation runbooks or a simple cron job.

Can I use Azure Container Instances instead of a VM?

ACI works for stateless containers but is not ideal for OpenClaw because it lacks persistent local storage between restarts. A VM with a managed disk gives you reliable state persistence and better control over the runtime environment.

SuperBuilder

Prefer a managed experience?

SuperBuilder runs OpenClaw with zero setup — cloud execution, cost tracking, and team collaboration built in.

Try SuperBuilder Free