Deploy on Azure
One Cloud Shell script provisions an AKS cluster and installs the whole stack.
The script registers the resource providers, provisions the cluster, installs the bundled
Helm chart (gateway, optimizer, console, Postgres), and exposes the gateway API (:8787)
and console (:3000) as load balancers scoped to the CIDR you choose. Already run your
own cluster? Use Kubernetes (Helm) instead: AKS is an ordinary
conformant cluster, and
The two files you author install the same
chart with nothing Azure-specific in them. The only AKS-specific part:
az group create --name anyray-rg --location eastus
az aks create --resource-group anyray-rg --name anyray \
--node-count 2 --enable-managed-identity --generate-ssh-keys
az aks get-credentials --resource-group anyray-rg --name anyray
helm install anyray oci://public.ecr.aws/anyray/anyray \
-f my-values.yaml --namespace anyray --create-namespace
AKS ships a default-annotated default StorageClass (Azure Disk CSI), so the chart's
volumes bind with nothing to pre-create.
Prerequisites
- An Azure subscription with billing enabled and permission to create AKS, Virtual
Network, public IP, load balancer, and managed-disk resources. The script registers the
Microsoft.ContainerServiceandMicrosoft.Networkresource providers for you. - A narrow
ALLOWED_CIDR: your office or VPN range. For a single workstation:curl -fsS https://api.ipify.org, then<that-ip>/32. The script rejects0.0.0.0/0; the console and gateway carry your org's spend data and admin access. - An Anyray deployment token (
adt_…) from app.anyray.ai (setup wizard, or Settings → Deployments → New deployment).
Azure Cloud Shell already has az, kubectl, and helm; nothing to install locally.
Install
Any machine with the Azure CLI (az), kubectl, and helm works too; see Install
from your own terminal below.
git clone https://github.com/anyrayHQ/install anyray && cd anyray
export ALLOWED_CIDR="203.0.113.0/24" # your office / VPN range
export DEPLOYMENT_TOKEN="adt_..." # from app.anyray.ai
# Optional overrides:
# export LOCATION="eastus"
# export RESOURCE_GROUP="anyray"
# export CLUSTER="anyray"
# export NAMESPACE="anyray"
# export IMAGE_TAG="latest" # or pin vX.Y.Z
./azure/deploy.sh
The script registers the resource providers, creates the resource group and AKS
cluster, generates the secrets (ANYRAY_ADMIN_TOKEN, ANYRAY_CONTENT_KEY, the
pseudonym salt, stored as the anyray-secrets Kubernetes Secret, never printed),
installs the chart, waits for the load balancer IPs, and prints your URLs and admin
key.
The AKS cluster is the long pole (~5–8 min), then the load balancers take ~1–3 min to get public IPs. The gateway restarts until Postgres is reachable, then goes healthy.
Open the printed Console URL and sign in with the printed admin key. Read the key again any time:
kubectl get secret anyray-secrets -n anyray \
-o jsonpath='{.data.ANYRAY_ADMIN_TOKEN}' | base64 --decode; echo
export GATEWAY_URL="http://your-gateway-lb-ip:8787"
export ADMIN_TOKEN="..." # from the deploy output
curl -fsS "$GATEWAY_URL/" && echo "gateway ok"
# Deployment health: gateway / observability / spend / optimizer / portal:
curl -fsS "$GATEWAY_URL/admin/health" -H "Authorization: Bearer ${ADMIN_TOKEN}"
/admin/health returns 503 and names the failing leg if any required service is
down; a healthy stack returns "ok": true with every leg green.
Everything above runs in your Azure subscription. Point your local coding tools at the
gateway with npx anyray-connect@latest --gateway <GatewayURL>
(developer FAQ).
Install from your own terminal
From any machine with the Azure CLI (az), kubectl, and helm authenticated to your
subscription (az login):
git clone https://github.com/anyrayHQ/install anyray && cd anyray
export SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"
export ALLOWED_CIDR="203.0.113.0/24" # your office / VPN range
export DEPLOYMENT_TOKEN="adt_..." # from app.anyray.ai
./azure/deploy.sh
The script is idempotent: re-running it reuses an existing cluster and keeps the
existing anyray-secrets rather than rotating the content key. The inputs:
| Variable | Required | Default | What it is |
|---|---|---|---|
SUBSCRIPTION_ID | no | az's active subscription | Azure subscription to deploy into. |
ALLOWED_CIDR | yes | n/a | CIDR allowed to reach the console/gateway LBs; 0.0.0.0/0 is rejected. |
DEPLOYMENT_TOKEN | yes | n/a | Anyray Billing app deployment token (adt_…). |
LOCATION | no | eastus | Azure region. |
RESOURCE_GROUP | no | anyray | Resource group (created if missing, reused if present). |
CLUSTER | no | anyray | AKS cluster name (reused if present). |
NAMESPACE | no | anyray | Kubernetes namespace. |
NODE_VM_SIZE | no | Standard_D2s_v5 | Node pool VM size. |
NODE_COUNT | no | 2 | Node pool size. |
IMAGE_TAG | no | latest | Anyray image tag; pin vX.Y.Z for a reproducible deploy. |
DEFAULT_MODEL | no | anthropic/claude-sonnet-4-5 | Target for the anyray-default model alias. |
Configuration
Gateway hardening and optimizer tuning are ordinary Helm values; set them in
my-values.yaml or pass --set on a helm upgrade and the gateway rolls with the new
value. The values and their defaults:
gateway hardening. To re-scope the load
balancers to a different CIDR later, see Troubleshoot below.
Troubleshoot
A LoadBalancer Service has no external IP
AKS provisions the public IP in ~1–3 min:
kubectl get svc gateway anyray-proxy -n anyray
If EXTERNAL-IP stays <pending> for several minutes, confirm the subscription has
public-IP quota in the region and that the cluster's managed identity has the Network
Contributor role on the AKS-managed node resource group
(MC_<resource-group>_<cluster>_<location>).
The console or gateway is unreachable from your network
The load balancers only admit traffic from ALLOWED_CIDR. Confirm your current public IP
is inside that range (curl -fsS https://api.ipify.org), and re-scope if needed:
helm upgrade anyray ./helm -f my-values.yaml --namespace anyray --reuse-values \
--set gateway.service.loadBalancerSourceRanges[0]=<your-cidr> \
--set proxy.service.loadBalancerSourceRanges[0]=<your-cidr>
A pod is stuck Pending or CrashLooping
kubectl get pods -n anyray
kubectl describe pod -n anyray <pod>
kubectl logs -n anyray deployment/anyray-gateway
A Pending Postgres or *-data PVC usually means no default StorageClass. AKS ships a
default-annotated default class (Azure Disk CSI); confirm with
kubectl get storageclass. A gateway that keeps restarting is most often still waiting on
Postgres.
Upgrade
IMAGE_TAG=latest follows the moving release channel. Re-run ./azure/deploy.sh to
converge, or pin a version and roll the chart directly; a pinned :vX.Y.Z keeps rollbacks
reproducible and auditable:
helm upgrade anyray ./helm -f my-values.yaml \
--namespace anyray --reuse-values --set image.tag=vX.Y.Z