Deploy on Google Cloud
One Cloud Shell script provisions a GKE Autopilot cluster and installs the whole stack.
The script 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: GKE is an ordinary conformant cluster, and
The two files you author install the same
chart with nothing Google-specific in them. The only GKE-specific part:
gcloud container clusters create-auto anyray --region us-central1
gcloud container clusters get-credentials anyray --region us-central1
helm install anyray oci://public.ecr.aws/anyray/anyray \
-f my-values.yaml --namespace anyray --create-namespace
Autopilot's default standard-rwo StorageClass satisfies the chart's volumes, so there is
nothing to pre-create.
Prerequisites
- A Google Cloud project with billing enabled and permission to create GKE, Compute Engine, and load balancer resources. The script enables the Kubernetes Engine and Compute Engine APIs 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).
Cloud Shell already has gcloud, kubectl, and helm; nothing to install locally.
Install
Clones the install repo into Cloud Shell and opens the guided walkthrough.
Open in Cloud ShellAny machine with the gcloud SDK, kubectl, and helm works too; see Install from
your own terminal below.
export ALLOWED_CIDR="203.0.113.0/24" # your office / VPN range
export DEPLOYMENT_TOKEN="adt_..." # from app.anyray.ai
# Optional overrides:
# export REGION="us-central1"
# export CLUSTER="anyray"
# export NAMESPACE="anyray"
# export IMAGE_TAG="latest" # or pin vX.Y.Z
./gcp/deploy.sh
The script enables the APIs, creates the Autopilot 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 Autopilot cluster is the long pole (~5–8 min), then the load balancers take ~1–3 min to get external 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 Google Cloud project. 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 gcloud SDK, kubectl, and helm authenticated to your project:
git clone https://github.com/anyrayHQ/install anyray && cd anyray
export PROJECT_ID="my-gcp-project"
export ALLOWED_CIDR="203.0.113.0/24" # your office / VPN range
export DEPLOYMENT_TOKEN="adt_..." # from app.anyray.ai
./gcp/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 |
|---|---|---|---|
PROJECT_ID | yes | gcloud's active project | GCP project 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_…). |
REGION | no | us-central1 | Autopilot region. |
CLUSTER | no | anyray | Cluster name (reused if present). |
NAMESPACE | no | anyray | Kubernetes namespace. |
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
GKE provisions the external IP in ~1–3 min:
kubectl get svc gateway anyray-proxy -n anyray
If EXTERNAL-IP stays <pending> for several minutes, confirm the Compute Engine API is
enabled and the project has quota for external IP addresses.
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. Autopilot
ships standard-rwo; 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 ./gcp/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