Deploy on Azure AKS
Install a self-hosted deployment into your own Azure subscription, on an AKS cluster with a sandbox-capable node.
Audience: IT (the enabler)
This guide assumes you are comfortable operating AKS. It does not cover general Kubernetes administration.
Before you start, read Cluster and node requirements. The cluster this guide builds is not a generic one, and its storage and runtime prerequisites are the part most likely to catch you out.
Reference Terraform is available.
The v3/azure-aks/reference-terraform (opens in a new tab) directory of the self-hosted resources repository provisions everything in Part 1, installs the sandbox runtime, creates each deployment's database and roles, and renders a completed values file for each deployment, so installing the application is the one step left to you.
It serves each deployment at an Azure-generated Azure Front Door hostname, the option in Step 1, so a proof of concept needs no domain or certificate of your own; its README explains the limits of that choice.
Treat it as a starting point to adapt, not a shape to keep: give your coding agent the directory, this guide, and your organization's standards, for example "Adapt this Terraform to our conventions: our naming and tagging, our existing virtual network, our state backend, and a hostname on our domain with its certificate in our Key Vault. Keep everything the deployment guide requires. Show me the plan before you change anything."
Review the result against Part 1: the node shape, the Kubernetes version, the workload identity federation, and the database roles are requirements; naming, networking, and the tool you provision with are yours to choose.
Part 1: Pre-deployment bill of materials
Hand this to procurement and your cloud and security teams on day 0.
Decide these carefully, they are painful to retrofit:
- Hostname. TLS, the OIDC redirect URI, and ingress all derive from it. Changing it later means re-issuing certificates and reconfiguring your identity provider.
- Region. Moving regions later means a rebuild.
Components
| Category | Component | Spec | Notes |
|---|---|---|---|
| Compute | AKS cluster | AKS 1.36 or newer, with the OIDC issuer, workload identity, and the managed Gateway API enabled, and the application routing add-on installed with its Gateway API implementation | Metrics Server and the Azure Disk CSI driver are built in. See Cluster and node requirements |
| Compute | Application node | A single x86_64 Standard_D32s_v5 (32 vCPU, 128 GiB), in a node pool fixed at one node with autoscaling off. It is the cluster's only node pool, so AKS system pods share the node | Hosts the application and every sandbox run. Not interchangeable with a general-purpose node pool. See Node sizing |
| Storage | Node OS disk | Managed disk, 200 GiB | Sized for the node image and container images |
| Storage | Data root StorageClass | Created by the application chart on the Azure Disk CSI driver: Premium SSD (Premium_LRS), encrypted at rest with platform-managed keys, WaitForFirstConsumer, expandable, reclaimPolicy: Delete | Nothing to create. Overridable with a custom class when you need provisioned IOPS or a customer-managed key; see Advanced configuration |
| Storage | Data root volume | A Premium SSD managed disk, 100 GiB by default, that a Pod of the application claims from that StorageClass on the node | A cache for materialized workspaces and sandbox images, not a system of record. Deleted with the node; a replacement starts cold. See The data root and Data root performance |
| Network | Virtual network | A node subnet for the cluster (Azure CNI Overlay) with the Microsoft.Storage service endpoint, and a subnet delegated to PostgreSQL Flexible Server | With CNI Overlay, the database and the storage account see the application's traffic from the node subnet |
| Network | Cluster ingress | A Gateway on the application routing add-on's Gateway API implementation, with the TLS certificate for your hostname in Azure Key Vault | You operate the Gateway and terminate TLS there. See Ingress |
| Network | DNS record | Your chosen hostname resolving to the Gateway's load balancer | Not needed with an Azure-generated hostname; see Step 1 |
| Data | PostgreSQL | Azure Database for PostgreSQL Flexible Server, PostgreSQL 17 or 18, General Purpose tier with at least 4 vCores, private access only. Allow-list PGCRYPTO and CITEXT in the azure.extensions server parameter | Primary application data store. A deployment holds well over a hundred connections across its pools, so check max_connections allows for it |
| Data | Blob storage | A general-purpose v2 storage account with one private container for the deployment, public blob access disabled, and network access limited to the node subnet | Durable storage for files, artifacts, and the workspace filesystem |
| Data | Key Vault key | An RSA key in Azure Key Vault, for example 3072-bit, that permits the encrypt, decrypt, wrap key, and unwrap key operations | Reserved for envelope encryption of secrets at rest. The values file requires it now, so the infrastructure is in place before that feature ships. Treat it as durable from the start; see Step 3 |
| Identity | OIDC IdP app | An application registered in Entra ID, Okta, or Auth0 | See Identity providers (OIDC) |
| Identity | Workload identity | A user-assigned managed identity with Storage Blob Data Contributor on the container and Key Vault Crypto User on the key, federated with a Kubernetes service account in the application namespace | Create it before the install and name the service account in the values file in Step 6 |
| Identity | Gateway certificate identity | A second user-assigned managed identity with Key Vault Secrets User on the vault that holds the TLS certificate, federated with a service account in the Gateway's namespace | The add-on reads the certificate as this identity. Kept apart from the application's, which needs no access to the certificate |
| Access | Azure and AKS admin access | Permissions for the deploying administrator, plus the Azure CLI, kubectl, and helm on their workstation | |
| Egress | Outbound HTTPS | Unrestricted egress recommended | Needed for the Sema4.ai services, the sandbox runtime's chart and image, and everything you connect the platform to. See Network endpoints |
Node sizing
The application node is a single Standard_D32s_v5: 32 vCPU and 128 GiB on Intel processors.
It carries the whole platform and every concurrent sandbox run, with headroom for several teams sharing the platform; see Allocatable capacity for what actually reaches the platform.
On Azure, nested virtualization is a property of the VM size, not a setting: Standard_D32s_v5 supports it, and there is nothing to enable.
If you choose a different size, confirm in the Azure documentation that it supports nested virtualization before you create the node pool.
This release does not scale horizontally, so the node is the ceiling on concurrent agent work for the life of the deployment. Replacing the node costs downtime and a cold cache rather than data. See Caveats and limits.
Data root performance
Premium SSD performance scales with disk size. At the 100 GiB default the data root is a P10 disk, with 500 IOPS and 100 MB/s; a 512 GiB data root is a P20, with 2,300 IOPS and 150 MB/s. Materialization is metadata-heavy, so a data root that is short of IOPS shows up as slower runs rather than as an error.
Raise the size with vfs.dataRoot.size when you need more, at install or later, without a restart.
For IOPS that do not depend on size, use a StorageClass of your own; see Advanced configuration.
Ingress
We recommend Microsoft's managed ingress for AKS, the application routing add-on's Gateway API implementation (opens in a new tab), and this guide builds on it. You create one Gateway, which terminates TLS with your certificate from Key Vault, and the application attaches its routes to it.
It is the only ingress configuration we test on Azure. The add-on's managed NGINX is built on the upstream ingress-nginx project, which is end of life (opens in a new tab).
The application itself works with any ingress controller or Gateway API implementation that meets the requirements in Using your own ingress, and the values file names it in place of the Gateway.
Part 2: Deployment steps
Step 1: Pick the hostname
Choose the hostname on a domain you control, for example finance-agents.company.com.
Whether it resolves publicly or only inside your network is your choice.
TLS, the OIDC redirect URI, and the Gateway configuration all derive from it.
You point DNS at the Gateway in Step 8.
Import or issue a TLS certificate for the hostname into Azure Key Vault, and note its identifier, https://<vault>.vault.azure.net/certificates/<name>.
Or use a hostname that Azure generates
You can use an Azure-generated hostname instead of one on your own domain.
Azure Front Door (opens in a new tab) (Standard or Premium) gives each endpoint a hostname of the form <endpoint>-<hash>.z01.azurefd.net, and serves it over HTTPS with a certificate that Microsoft issues and rotates.
You need no domain, no DNS record, and no certificate of your own, which suits a proof of concept.
The reference Terraform takes this approach, and its README describes what it trades away.
Azure generates the hostname when you create the endpoint, so create the Front Door endpoint first and use its hostname wherever this guide asks for yours. By default, an endpoint that you delete and recreate with the same name in the same Entra ID tenant gets the same hostname back. Moving to a hostname of your own later is a hostname change like any other; see the warning in Part 1.
With a Front Door hostname, TLS terminates at Front Door instead of at the Gateway, and some later steps change:
- Step 1: skip the certificate.
- Step 3: give the Gateway an HTTP listener on port 80 with no certificate and no hostname, and make it the origin of the Front Door endpoint. Front Door reaches it over plain HTTP, because Front Door accepts an HTTPS origin only with a publicly trusted certificate for the origin's hostname. Use a network security group to admit only the
AzureFrontDoor.Backendservice tag to the Gateway's load balancer. The Gateway certificate identity is not needed. - Step 6: set
sectionNamein the values file to the name of the HTTP listener. - Step 8: skip it; the hostname already resolves to Front Door.
Front Door's own limits then apply to every request: it waits at most 240 seconds for a response from the origin, and it closes idle and long-lived WebSocket connections on its own schedule (opens in a new tab).
Step 2: Register the OIDC application
Register an OIDC application in your identity provider and capture the Discovery URL, Client ID, and Client Secret.
Configure it with:
- Callback URL:
https://<hostname>/api/v1/auth/callback - Logout URL:
https://<hostname> - Application login URI:
https://<hostname>/login - Allowed web origin:
https://<hostname> - Scopes:
openid,profile,email
Follow the guide for your provider: Microsoft Entra ID · Auth0 · Okta.
Step 3: Provision Azure infrastructure
Provision the infrastructure using the bill of materials as the spec. The end state must include:
- An AKS cluster, version 1.36 or newer, with the OIDC issuer, workload identity, the managed Gateway API, and the application routing add-on's Gateway API implementation enabled, and
kubectlaccess configured against it. - The application node, a
Standard_D32s_v5with a 200 GiB OS disk, as the only node in a node pool of one. - PostgreSQL Flexible Server, version 17 or 18, in private access mode in the cluster's virtual network, with
PGCRYPTOandCITEXTin itsazure.extensionsparameter. - A storage account and a blob container, reachable from the node subnet.
- A Key Vault with an RSA key that permits encrypt, decrypt, wrap key, and unwrap key, and the TLS certificate from Step 1.
- The application namespace, a Kubernetes service account in it, and a user-assigned managed identity that holds Storage Blob Data Contributor on the container and Key Vault Crypto User on the key, federated with that account. You name the account in the values file in Step 6.
- The Gateway, in a namespace of its own, serving your hostname over HTTPS with the certificate from Step 1. You name it in the values file in Step 6.
Node and storage checks
Node bootstrap needs nothing platform-specific: no custom data, no script, no packages, and no data volume. The node runs the stock AKS node image, and nested virtualization comes from the VM size. Bootstrap does not install the sandbox runtime; that is Step 4, and it prepares nodes that join later on its own.
There is no StorageClass to create: the chart creates the data root's class itself, as described in the bill of materials.
Confirm the node and the storage prerequisites:
- The node is
Ready. /dev/kvmis present on the node.kubectl get csidriver disk.csi.azure.comreturns the driver.
Check /dev/kvm before you install the sandbox runtime. Nothing
downstream notices a missing device: the runtime installs and reports
success, the platform comes up healthy, and only agent runs fail. The
reference Terraform ships a one-shot check,
k8s/kvm-check.yaml (opens in a new tab).
Save it, run kubectl apply -f kvm-check.yaml, and read
kubectl -n default logs job/kvm-check: a healthy node lists the /dev/kvm
device and the vmx CPU flag.
Application identity
The federation is the part to get exactly right. The service account carries the identity's client ID. The identity trusts the cluster's OIDC issuer for exactly that namespace and account:
kubectl create namespace <namespace>
kubectl -n <namespace> create serviceaccount <service-account>
kubectl -n <namespace> annotate serviceaccount <service-account> \
azure.workload.identity/client-id=<identity-client-id>
az identity federated-credential create \
--resource-group <resource-group> \
--identity-name <identity-name> \
--name <namespace>-<service-account> \
--issuer "$(az aks show --resource-group <resource-group> --name <cluster> \
--query oidcIssuerProfile.issuerUrl --output tsv)" \
--subject system:serviceaccount:<namespace>:<service-account> \
--audiences api://AzureADTokenExchangeThe workload identity needs Key Vault Crypto User on the RSA key. The key
must permit encrypt, decrypt, wrap key, and unwrap key, the operations that
envelope encryption of secrets at rest and encrypting small values directly
under the key call, and the role grants them. With them granted now, those
features need no infrastructure change. On a vault that uses access policies
instead of Azure RBAC, grant the Get, Encrypt, Decrypt, Wrap Key, and Unwrap
Key key permissions instead. If you limit the vault's network access, add a
virtual network rule for the node subnet, which needs the
Microsoft.KeyVault service endpoint. Treat the key as durable from the
start: once secrets are wrapped with it, deleting the key makes them
unreadable. Soft delete keeps a deleted key recoverable for the vault's
retention period; for a production deployment, also turn on purge
protection, which blocks an early purge.
Ingress and the Gateway
Turn on the ingress (Azure CLI 2.86.0 or newer).
The first command installs the application routing add-on with its NGINX controller off and the Key Vault secrets provider on; on a cluster that already has the add-on, run az aks approuting update with the same flags.
The second installs the managed Gateway API definitions and the add-on's implementation of them.
The third lets the secrets provider pick up a renewed certificate:
az aks approuting enable --resource-group <resource-group> --name <cluster> \
--nginx None --enable-kv
az aks update --resource-group <resource-group> --name <cluster> \
--enable-gateway-api --enable-app-routing-istio
az aks addon update --resource-group <resource-group> --name <cluster> \
--addon azure-keyvault-secrets-provider --enable-secret-rotationkubectl get gatewayclass approuting-istio should show the class as accepted.
The add-on reads the TLS certificate from Key Vault as an identity you give it, federated with a service account in the Gateway's namespace. Create the namespace and the account, grant the Gateway certificate identity from Part 1 Key Vault Secrets User on the vault, and federate it with the account:
kubectl create namespace <gateway-namespace>
kubectl -n <gateway-namespace> create serviceaccount <gateway-service-account>
kubectl -n <gateway-namespace> annotate serviceaccount <gateway-service-account> \
azure.workload.identity/client-id=<gateway-identity-client-id>
kubectl -n <gateway-namespace> label serviceaccount <gateway-service-account> \
azure.workload.identity/use=true
az role assignment create \
--assignee-object-id <gateway-identity-principal-id> \
--assignee-principal-type ServicePrincipal \
--role "Key Vault Secrets User" \
--scope <key-vault-resource-id>
az identity federated-credential create \
--resource-group <resource-group> \
--identity-name <gateway-identity-name> \
--name <gateway-namespace>-<gateway-service-account> \
--issuer "$(az aks show --resource-group <resource-group> --name <cluster> \
--query oidcIssuerProfile.issuerUrl --output tsv)" \
--subject system:serviceaccount:<gateway-namespace>:<gateway-service-account> \
--audiences api://AzureADTokenExchangeThen create the Gateway with kubectl apply -f.
It has one HTTPS listener for your hostname, named https, and admits routes from the application namespace only:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: <gateway>
namespace: <gateway-namespace>
spec:
gatewayClassName: approuting-istio
listeners:
- name: https
hostname: <hostname>
port: 443
protocol: HTTPS
tls:
mode: Terminate
options:
# The certificate from Step 1. Leave the version off so a renewed
# certificate is picked up.
kubernetes.azure.com/tls-cert-keyvault-uri: https://<vault>.vault.azure.net/certificates/<name>
kubernetes.azure.com/tls-cert-service-account: <gateway-service-account>
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: <namespace>The Gateway's own namespace keeps its load balancer, and so the address your DNS record points at, in place when you reinstall the application. The load balancer is public by default. To keep it private, add the internal load balancer annotation to the Gateway's spec:
spec:
infrastructure:
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"Wait for the add-on to provision the Gateway, then confirm it synced the certificate into a Secret named kv-gw-cert-<gateway>-https:
kubectl -n <gateway-namespace> wait --for=condition=programmed gateway/<gateway> --timeout=300s
kubectl -n <gateway-namespace> get secretFor more on this setup, including publishing the DNS record from Azure DNS automatically, see Configure Azure DNS and TLS with the application routing Gateway API implementation (opens in a new tab).
Step 4: Install the sandbox runtime
Install Kata Containers into the cluster, once, following Install the sandbox runtime. Verify it before continuing:
kubectl get runtimeclass kata-clh
kubectl get nodes -l katacontainers.io/kata-runtime=trueNothing checks this at install time. The application may install without the sandbox runtime, but it will not operate correctly: agent runs fail, and the failure does not point back at the missing runtime. Make sure both commands above pass before you proceed.
Step 5: Create the database and its roles
The application does not create its own database, and it connects with three least-privilege roles rather than the server administrator. The server has no public endpoint, so connect from inside the virtual network. A throwaway Pod in the cluster works, and prompts for the administrator password:
kubectl run psql --rm -it --restart=Never --image=postgres:18-alpine -- \
psql -h <server>.postgres.database.azure.com -U <admin-user> -d postgresRun the following, with your own names and strong passwords:
CREATE ROLE <migrator_role> LOGIN PASSWORD '<migrator_password>'
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
CREATE ROLE <definer_role> NOLOGIN
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION BYPASSRLS;
CREATE ROLE <app_role> LOGIN PASSWORD '<app_password>'
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
GRANT <definer_role> TO <migrator_role>;
CREATE DATABASE <database>;
GRANT <migrator_role> TO CURRENT_USER;
ALTER DATABASE <database> OWNER TO <migrator_role>;
GRANT CONNECT ON DATABASE <database> TO <app_role>, <definer_role>;
REVOKE <migrator_role> FROM CURRENT_USER;The migrator role applies the schema on install and upgrade, the app role is what the application connects as at runtime, and the definer role owns privileged functions and cannot log in; see The database roles for the full rationale.
The migrator enables the pgcrypto and citext extensions itself, which is why both must be on the server's allow-list.
The Flexible Server administrator is not a PostgreSQL superuser, but on PostgreSQL 16 and newer it can create the definer role with BYPASSRLS.
Capture the database name, the three role names, and the two passwords: they go into the values file.
Step 6: Prepare your values file
The values file is your readiness check. Complete it before you install.
Every value in it is something a prerequisite from Part 1 produced, so a value
you cannot fill in is a prerequisite that is not ready. Do not start Step 7
with a REPLACE_ME left in the file.
Start from the template below and replace every REPLACE_ME.
Only what the chart cannot know about your environment is in it; everything else is a chart default, tuned for the supported node.
Keep the file: you reuse it for every upgrade.
The template is for application version 3.1.4 or later; do not pass an older version to helm install in Step 7.
my-values-v3-azure-aks.example.yaml# Helm values for a self-hosted deployment on Azure AKS.
# Requires application version 3.1.4 or later.
#
# Complete every REPLACE_ME before you install; a value you cannot fill in is
# a prerequisite that is not ready yet. Keep this file: you reuse it for
# upgrades. Everything not set here is a chart default, tuned for the
# supported 32 vCPU / 128 GiB node.
# Your hostname from Step 1 as https://<hostname>: scheme, host, and an
# optional port, with no path. The chart derives every URL the platform
# needs about itself from it: the sign-in callback, the allowed browser
# origin, and the webhook callback URLs. The URLs you registered in your
# identity provider in Step 2 are built from the same hostname, and
# httpRoute.hostnames below repeats it. Each derived URL has an explicit
# override; see Advanced configuration.
applicationUrl: https://REPLACE_ME
# The Kubernetes service account that gives the application access to Azure
# resources. Create it in the application namespace, annotate it with the
# client ID of the user-assigned managed identity
# (azure.workload.identity/client-id), and federate the identity with it.
serviceAccount:
create: false
name: REPLACE_ME
# The application database and its three roles, created before the install
# (see "Create the database and its roles" in the AKS guide).
postgres:
host: REPLACE_ME
database: REPLACE_ME
appRole: REPLACE_ME
appPassword: REPLACE_ME
definerRole: REPLACE_ME
migratorRole: REPLACE_ME
migratorPassword: REPLACE_ME
# The Azure resources from the bill of materials, in one block. From these the
# chart derives everything Azure-shaped:
# - object storage at abfss://<container>@<account>.dfs.core.windows.net,
# plus the optional prefix, reached as the managed identity federated with
# the service account above (no account keys);
# - a Premium SSD StorageClass on the Azure Disk CSI driver, encrypted at
# rest with platform-managed keys, and the data root claimed from it as a
# raw block volume on the node (100 GiB by default; raise
# vfs.dataRoot.size later and upgrade, the filesystem grows without a
# restart);
# - the sandbox wiring for AKS: the Kata runtime you installed before the
# platform, reached through the node's containerd, with the release
# installing no Kata of its own;
# - no Ingress: the application is served through the HTTPRoute below.
# Anything set explicitly elsewhere in this file wins over a derived value.
infrastructure:
platform: azure
azure:
# The storage account name only: 3 to 24 lowercase letters and digits.
storageAccountName: REPLACE_ME
# The container name only. The optional key prefix keeps several
# deployments apart in one container.
blobContainerName: REPLACE_ME
# blobKeyPrefix: ""
# Identifier of the RSA key in Key Vault reserved for envelope encryption
# of secrets at rest, as https://<vault>.vault.azure.net/keys/<name>. The
# chart requires it. Leave the version off to follow the key's rotation.
# The key must permit encrypt, decrypt, wrap key, and unwrap key, and the
# managed identity needs Key Vault Crypto User on it.
keyVaultKeyUrl: REPLACE_ME
api:
config:
# Encryption keys, yours to generate and to keep. Generate a long random
# string for each, for example with `openssl rand -hex 32`.
# secretsKeys encrypts the credentials the platform stores in its
# database: model platform keys, integration and OAuth tokens. The
# database outlives the cluster, and without the exact key its encrypted
# contents cannot be decrypted; the database may become unusable.
# projectPortabilityKeys protects exported project archives the same
# way. Keep this file backed up somewhere safe outside the cluster.
secretsKeys: '{"v1":"REPLACE_ME"}'
projectPortabilityKeys: '{"active":"REPLACE_ME"}'
# The OIDC application you registered against your hostname in Step 2.
auth:
oidc:
server: REPLACE_ME # your provider's discovery URL; the bare issuer URL also works
clientId: REPLACE_ME
clientSecret: REPLACE_ME
# Routing through the Gateway you created in Step 3, on the AKS application
# routing add-on's Gateway API implementation. The chart renders an HTTPRoute
# for the application's paths and attaches it to the Gateway's HTTPS
# listener, where TLS terminates with your certificate from Key Vault.
httpRoute:
enabled: true
parentRefs:
- name: REPLACE_ME # the Gateway's name
namespace: REPLACE_ME # the Gateway's namespace
sectionName: https # the listener's name
hostnames:
# Your hostname from Step 1, as on the Gateway's listener.
- REPLACE_METhree things are worth a moment.
The data root size is not in the file because 100 GiB is the chart default; you can raise it later (vfs.dataRoot.size) without a restart, and on Azure a larger disk is also a faster one (see Data root performance).
Every value the infrastructure block or applicationUrl derives has an explicit override that wins over the convention (a StorageClass of your own, vfs.blobs.uri set directly, or one of the derived URLs set on its own key), but the file above is the supported shape; see Advanced configuration.
The data root mounts at /var/lib/blockparty/<release>, named after the Helm release you install under; nothing in the file needs to say so.
And no internal service tokens are in the file: the chart generates them at first install, reuses them across upgrades, and they die with the deployment.
The two encryption keys are in the file, deliberately: they protect what the platform stores in your database, so their custody stays with you and this file, not with the cluster.
Step 7: Install the application
The self-hosted release channel for version 3 is not open yet. There is no install command to publish. This step will carry the registry login and the chart install once the channel ships.
Install into the namespace that holds the service account from Step 3, not into a new one.
Expect the VFS and sandbox Pods to sit in ContainerCreating for a few minutes on a first install: they bind the data root, and they start once the data-root workload has claimed, formatted, and mounted it.
Step 8: Point DNS at the Gateway
Read the load balancer address from the Gateway:
kubectl -n <gateway-namespace> get gateway <gateway> \
-o jsonpath='{.status.addresses[0].value}{"\n"}'Create the DNS record resolving your hostname to that address. Sign-in does not complete until the hostname resolves and TLS is serving.
Step 9: Validate
- Check the data root: the claim is
Boundand thedata-rootlog ends withdata root ready. See Verify the data root. - Check the workload identity:
kubectl -n <namespace> describe pod -l app.kubernetes.io/component=vfs | grep AZURE_listsAZURE_CLIENT_ID,AZURE_TENANT_ID, andAZURE_FEDERATED_TOKEN_FILE. If they are missing, the service account annotation or the federated credential is not in place, and blob storage fails on first use rather than at startup. - Check the route:
kubectl -n <namespace> get httproute -o jsonpath='{range .items[*].status.parents[*].conditions[*]}{.type}={.status} {end}{"\n"}'showsAccepted=TrueandResolvedRefs=True. IfAcceptedisFalse, the Gateway does not admit routes from the application namespace, or the hostname does not match its listener. - Browse to
https://<hostname>. Sign-in should redirect to your identity provider. - Sign in with a permitted user and confirm you land in the workspace.
The first user of a new deployment becomes its owner. Sign in yourself first, before opening access more widely.
Step 10: Back up the encryption keys
The two encryption keys you generated into the values file encrypt the secrets the platform stores in its database (model platform credentials, integration and OAuth tokens), and the database outlives the cluster.
Anything encrypted with a lost key cannot be read back. Stored credentials and connections cannot be recovered, and the database may become unusable. Treat the keys as irreplaceable.
Store the two key values in your secrets manager, alongside your other infrastructure credentials, so they survive the loss of the machine that holds the values file. Treat them as root credentials.
Next steps
With the application running, continue to Administration to configure models, connect integrations and data sources, and set up teams. For day-2 behavior (resizing the data root, replacing the node, upgrading the sandbox runtime), see the Operations reference.