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:
az loginand registerMicrosoft.Compute,Microsoft.Network,Microsoft.ResourceHealthproviders- Create a resource group in your preferred region
- Create an NSG with rules: deny all inbound SSH (priority 100), allow AzureBastionSubnet SSH (110), allow outbound HTTPS (120)
- Create a VNet with a default subnet and an
AzureBastionSubnet - Deploy an Ubuntu 24.04
Standard_B2sVM with no public IP, attached to the NSG - Deploy Azure Bastion (Basic SKU) into the Bastion subnet
- SSH through Bastion, run the OpenClaw installer script
- Configure and start OpenClaw
Prerequisites
Before starting, make sure you have:
- An active Azure subscription with permissions to create resources
- The Azure CLI installed (
azversion 2.50 or later) - An Anthropic API key for OpenClaw's LLM backend
- Basic familiarity with Linux command-line operations
Verify your CLI installation:
Step 1: Authenticate and Configure Azure CLI
Sign in to your Azure account:
If you have multiple subscriptions, set the one you want to use:
Register the required resource providers. These may already be registered, but running the commands is idempotent:
Check registration status (wait until all show Registered):
Step 2: Create a Resource Group
All resources will live in a single resource group for easy management and cleanup:
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.
Add the security rules in priority order:
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.
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.
The --public-ip-address "" flag ensures the VM has no public IP. All SSH access goes through Bastion.
Verify the VM is running:
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:
Then deploy Bastion:
This takes 5-10 minutes to provision. Once complete, you can connect through the Azure Portal or via the CLI:
Step 7: Install OpenClaw
Once connected to the VM via Bastion, run the OpenClaw installer:
After installation completes, configure OpenClaw with your API key:
For persistent configuration, create the config file:
Enable OpenClaw as a systemd service so it starts on boot:
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:
With the tunnel active, open http://localhost:18789 in your browser.
Cost Breakdown
| Resource | Monthly Cost | Notes |
|---|---|---|
| Standard_B2s VM | ~$33 | 2 vCPU, 4 GB RAM, burstable |
| Premium SSD (30 GB) | ~$5 | P4 managed disk |
| Azure Bastion (Basic) | ~$140 | Billed per hour while deployed |
| VNet / NSG | Free | No charge for basic networking |
| Total (Bastion always on) | ~$178 | |
| Total (Bastion on-demand) | ~$55 | Delete 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:
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:
You can also set up auto-shutdown to deallocate the VM on a schedule:
Security Best Practices
- No public IP: The VM is only accessible through Azure Bastion, eliminating direct internet exposure.
- NSG layered rules: SSH is denied to all sources except the Bastion subnet. Even if Bastion is compromised, the blast radius is limited to the defined subnet.
- SSH keys only: Password authentication is disabled by default on the generated VM. Never enable it.
- Disk encryption: Enable Azure Disk Encryption for the OS disk if your compliance requirements mandate it:
- Update regularly: Set up unattended upgrades on the VM for security patches:
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:
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:
OpenClaw fails to start
Check the systemd service logs:
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:
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.