{"id":614,"date":"2026-03-21T22:52:57","date_gmt":"2026-03-21T14:52:57","guid":{"rendered":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/install-azure\/"},"modified":"2026-03-21T23:23:48","modified_gmt":"2026-03-21T15:23:48","slug":"install-azure","status":"publish","type":"post","link":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/install-azure\/","title":{"rendered":"Azure"},"content":{"rendered":"<h1>Azure<\/h1>\n<h1>OpenClaw on Azure Linux VM<\/h1>\n<p>This guide sets up an Azure Linux VM with the Azure CLI, applies Network Security Group (NSG) hardening, configures Azure Bastion for SSH access, and installs OpenClaw.<\/p>\n<h2>What you&#8217;ll do<\/h2>\n<ul>\n<li>Create Azure networking (VNet, subnets, NSG) and compute resources with the Azure CLI<\/li>\n<li>Apply Network Security Group rules so VM SSH is allowed only from Azure Bastion<\/li>\n<li>Use Azure Bastion for SSH access (no public IP on the VM)<\/li>\n<li>&#23433;&#35013; OpenClaw with the installer script<\/li>\n<li>Verify the Gateway<\/li>\n<\/ul>\n<h2>What you need<\/h2>\n<ul>\n<li>An Azure subscription with permission to create compute and network resources<\/li>\n<li>Azure CLI installed (see <a href=\"https:\/\/learn.microsoft.com\/cli\/azure\/install-azure-cli\">Azure CLI install steps<\/a> if needed)<\/li>\n<li>An SSH key pair (the guide covers generating one if needed)<\/li>\n<li>~20-30 minutes<\/li>\n<\/ul>\n<h2>Configure deployment<\/h2>\n<p>    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    az login<br \/>\n    az extension add -n ssh<\/code><\/p>\n<pre><code>The `ssh` extension is required for Azure Bastion native SSH tunneling.\n<\/code><\/pre>\n<\/p>\n<p>\n    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    az provider register --namespace Microsoft.Compute<br \/>\n    az provider register --namespace Microsoft.Network<\/code><\/p>\n<pre><code>Verify registration. Wait until both show `Registered`.\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz provider show --namespace Microsoft.Compute --query registrationState -o tsv\naz provider show --namespace Microsoft.Network --query registrationState -o tsv\n```\n<\/code><\/pre>\n<\/p>\n<p>\n    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    RG=\"rg-openclaw\"<br \/>\n    LOCATION=\"westus2\"<br \/>\n    VNET_NAME=\"vnet-openclaw\"<br \/>\n    VNET_PREFIX=\"10.40.0.0\/16\"<br \/>\n    VM_SUBNET_NAME=\"snet-openclaw-vm\"<br \/>\n    VM_SUBNET_PREFIX=\"10.40.2.0\/24\"<br \/>\n    BASTION_SUBNET_PREFIX=\"10.40.1.0\/26\"<br \/>\n    NSG_NAME=\"nsg-openclaw-vm\"<br \/>\n    VM_NAME=\"vm-openclaw\"<br \/>\n    ADMIN_USERNAME=\"openclaw\"<br \/>\n    BASTION_NAME=\"bas-openclaw\"<br \/>\n    BASTION_PIP_NAME=\"pip-openclaw-bastion\"<\/code><\/p>\n<pre><code>Adjust names and CIDR ranges to fit your environment. The Bastion subnet must be at least `\/26`.\n<\/code><\/pre>\n<\/p>\n<p>\n    Use your existing public key if you have one:<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\nSSH_PUB_KEY=\"$(cat ~\/.ssh\/id_ed25519.pub)\"\n```\n\nIf you don't have an SSH key yet, generate one:\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\nssh-keygen -t ed25519 -a 100 -f ~\/.ssh\/id_ed25519 -C \"you@example.com\"\nSSH_PUB_KEY=\"$(cat ~\/.ssh\/id_ed25519.pub)\"\n```\n<\/code><\/pre>\n<\/p>\n<p>\n    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    VM_SIZE=\"Standard_B2as_v2\"<br \/>\n    OS_DISK_SIZE_GB=64<\/code><\/p>\n<pre><code>Choose a VM size and OS disk size available in your subscription and region:\n\n* Start smaller for light usage and scale up later\n* Use more vCPU\/RAM\/disk for heavier automation, more channels, or larger model\/tool workloads\n* If a VM size is unavailable in your region or subscription quota, pick the closest available SKU\n\nList VM sizes available in your target region:\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz vm list-skus --location \"${LOCATION}\" --resource-type virtualMachines -o table\n```\n\nCheck your current vCPU and disk usage\/quota:\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz vm list-usage --location \"${LOCATION}\" -o table\n```\n<\/code><\/pre>\n<\/p>\n<h2>Deploy Azure resources<\/h2>\n<p>    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    az group create -n \"${RG}\" -l \"${LOCATION}\"<\/code>\n  <\/p>\n<p>\n    Create the NSG and add rules so only the Bastion subnet can SSH into the VM.<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz network nsg create \n  -g \"${RG}\" -n \"${NSG_NAME}\" -l \"${LOCATION}\"\n\n# Allow SSH from the Bastion subnet only\naz network nsg rule create \n  -g \"${RG}\" --nsg-name \"${NSG_NAME}\" \n  -n AllowSshFromBastionSubnet --priority 100 \n  --access Allow --direction Inbound --protocol Tcp \n  --source-address-prefixes \"${BASTION_SUBNET_PREFIX}\" \n  --destination-port-ranges 22\n\n# Deny SSH from the public internet\naz network nsg rule create \n  -g \"${RG}\" --nsg-name \"${NSG_NAME}\" \n  -n DenyInternetSsh --priority 110 \n  --access Deny --direction Inbound --protocol Tcp \n  --source-address-prefixes Internet \n  --destination-port-ranges 22\n\n# Deny SSH from other VNet sources\naz network nsg rule create \n  -g \"${RG}\" --nsg-name \"${NSG_NAME}\" \n  -n DenyVnetSsh --priority 120 \n  --access Deny --direction Inbound --protocol Tcp \n  --source-address-prefixes VirtualNetwork \n  --destination-port-ranges 22\n```\n\nThe rules are evaluated by priority (lowest number first): Bastion traffic is allowed at 100, then all other SSH is blocked at 110 and 120.\n<\/code><\/pre>\n<\/p>\n<p>\n    Create the VNet with the VM subnet (NSG attached), then add the Bastion subnet.<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz network vnet create \n  -g \"${RG}\" -n \"${VNET_NAME}\" -l \"${LOCATION}\" \n  --address-prefixes \"${VNET_PREFIX}\" \n  --subnet-name \"${VM_SUBNET_NAME}\" \n  --subnet-prefixes \"${VM_SUBNET_PREFIX}\"\n\n# Attach the NSG to the VM subnet\naz network vnet subnet update \n  -g \"${RG}\" --vnet-name \"${VNET_NAME}\" \n  -n \"${VM_SUBNET_NAME}\" --nsg \"${NSG_NAME}\"\n\n# AzureBastionSubnet &mdash; name is required by Azure\naz network vnet subnet create \n  -g \"${RG}\" --vnet-name \"${VNET_NAME}\" \n  -n AzureBastionSubnet \n  --address-prefixes \"${BASTION_SUBNET_PREFIX}\"\n```\n<\/code><\/pre>\n<\/p>\n<p>\n    The VM has no public IP. SSH access is exclusively through Azure Bastion.<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz vm create \n  -g \"${RG}\" -n \"${VM_NAME}\" -l \"${LOCATION}\" \n  --image \"Canonical:ubuntu-24_04-lts:server:latest\" \n  --size \"${VM_SIZE}\" \n  --os-disk-size-gb \"${OS_DISK_SIZE_GB}\" \n  --storage-sku StandardSSD_LRS \n  --admin-username \"${ADMIN_USERNAME}\" \n  --ssh-key-values \"${SSH_PUB_KEY}\" \n  --vnet-name \"${VNET_NAME}\" \n  --subnet \"${VM_SUBNET_NAME}\" \n  --public-ip-address \"\" \n  --nsg \"\"\n```\n\n`--public-ip-address \"\"` prevents a public IP from being assigned. `--nsg \"\"` skips creating a per-NIC NSG (the subnet-level NSG handles security).\n\n**Reproducibility:** The command above uses `latest` for the Ubuntu image. To pin a specific version, list available versions and replace `latest`:\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz vm image list \n  --publisher Canonical --offer ubuntu-24_04-lts \n  --sku server --all -o table\n```\n<\/code><\/pre>\n<\/p>\n<p>\n    Azure Bastion provides managed SSH access to the VM without exposing a public IP. Standard SKU with tunneling is required for CLI-based <code>az network bastion ssh<\/code>.<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\naz network public-ip create \n  -g \"${RG}\" -n \"${BASTION_PIP_NAME}\" -l \"${LOCATION}\" \n  --sku Standard --allocation-method Static\n\naz network bastion create \n  -g \"${RG}\" -n \"${BASTION_NAME}\" -l \"${LOCATION}\" \n  --vnet-name \"${VNET_NAME}\" \n  --public-ip-address \"${BASTION_PIP_NAME}\" \n  --sku Standard --enable-tunneling true\n```\n\nBastion provisioning typically takes 5-10 minutes but can take up to 15-30 minutes in some regions.\n<\/code><\/pre>\n<\/p>\n<h2>&#23433;&#35013; OpenClaw<\/h2>\n<p>    &#8220;`bash  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\n    VM_ID=&#8221;$(az vm show -g &#8220;${RG}&#8221; -n &#8220;${VM_NAME}&#8221; &#8211;query id -o tsv)&#8221;<\/p>\n<pre><code>az network bastion ssh \n  --name \"${BASTION_NAME}\" \n  --resource-group \"${RG}\" \n  --target-resource-id \"${VM_ID}\" \n  --auth-type ssh-key \n  --username \"${ADMIN_USERNAME}\" \n  --ssh-key ~\/.ssh\/id_ed25519\n```\n<\/code><\/pre>\n<\/p>\n<p>\n    <code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n    curl -fsSL https:\/\/openclaw.ai\/install.sh -o \/tmp\/install.sh<br \/>\n    bash \/tmp\/install.sh<br \/>\n    rm -f \/tmp\/install.sh<\/code><\/p>\n<pre><code>The installer installs Node LTS and dependencies if not already present, installs OpenClaw, and launches the onboarding wizard. See [Install](\/install) for details.\n<\/code><\/pre>\n<\/p>\n<p>\n    After onboarding completes:<\/p>\n<pre><code>```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\nopenclaw gateway status\n```\n\nMost enterprise Azure teams already have GitHub Copilot licenses. If that is your case, we recommend choosing the GitHub Copilot provider in the OpenClaw onboarding wizard. See [GitHub Copilot provider](\/providers\/github-copilot).\n<\/code><\/pre>\n<\/p>\n<h2>Cost considerations<\/h2>\n<p>Azure Bastion Standard SKU runs approximately <strong>$140\/month<\/strong> and the VM (Standard_B2as_v2) runs approximately <strong>$55\/month<\/strong>.<\/p>\n<p>To reduce costs:<\/p>\n<ul>\n<li><strong>Deallocate the VM<\/strong> when not in use (stops compute billing; disk charges remain). The OpenClaw Gateway will not be reachable while the VM is deallocated &mdash; restart it when you need it live again:<\/li>\n<\/ul>\n<p><code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\n  az vm deallocate -g \"${RG}\" -n \"${VM_NAME}\"<br \/>\n  az vm start -g \"${RG}\" -n \"${VM_NAME}\"   # restart later<\/code><\/p>\n<ul>\n<li>\n<p><strong>Delete Bastion when not needed<\/strong> and recreate it when you need SSH access. Bastion is the largest cost component and takes only a few minutes to provision.<\/p>\n<\/li>\n<li>\n<p><strong>Use the Basic Bastion SKU<\/strong> (~$38\/month) if you only need Portal-based SSH and don&#8217;t require CLI tunneling (<code>az network bastion ssh<\/code>).<\/p>\n<\/li>\n<\/ul>\n<h2>Cleanup<\/h2>\n<p>To delete all resources created by this guide:<\/p>\n<p><code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\naz group delete -n \"${RG}\" --yes --no-wait<\/code><\/p>\n<p>This removes the resource group and everything inside it (VM, VNet, NSG, Bastion, public IP).<\/p>\n<h2>&#19979;&#19968;&#27493;<\/h2>\n<ul>\n<li>Set up messaging channels: <a href=\"\/channels\">Channels<\/a><\/li>\n<li>Pair local devices as nodes: <a href=\"\/nodes\">Nodes<\/a><\/li>\n<li>Configure the Gateway: <a href=\"\/gateway\/configuration\">Gateway configuration<\/a><\/li>\n<li>For more details on OpenClaw Azure deployment with the GitHub Copilot model provider: <a href=\"https:\/\/github.com\/johnsonshi\/openclaw-azure-github-copilot\">OpenClaw on Azure with GitHub Copilot<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Azure OpenClaw on Azure Linux VM This guide sets up an  [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-614","post","type-post","status-publish","format-standard","hentry","category-docs"],"_links":{"self":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/comments?post=614"}],"version-history":[{"count":3,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/614\/revisions"}],"predecessor-version":[{"id":768,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/614\/revisions\/768"}],"wp:attachment":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/media?parent=614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/categories?post=614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/tags?post=614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}