Skip to main content

Deploy DIAL on Azure

Deploy the full DIAL stack to Azure Kubernetes Service (AKS) using the umbrella Helm chart, with Azure Blob Storage for files and Azure OpenAI as the model provider. This guide assumes you operate an AKS cluster and are familiar with Helm and kubectl. For the chart model and infrastructure mapping, read the Helm chart overview first.

Prerequisites​

  • An AKS cluster, version 1.24 or higher, with kubectl and Helm 3.8.0 or higher configured against it

  • The Ingress-NGINX controller and cert-manager installed in the cluster

  • Azure AD Workload Identity enabled on the cluster

  • An Azure Blob Storage account for DIAL file storage

  • An Azure OpenAI resource with at least one model deployed

  • A Microsoft Entra ID app registration for DIAL Chat sign-in, with a client secret

  • A domain and a cert-manager ClusterIssuer for TLS

  • The DIAL Helm repository added:

    helm repo add dial https://charts.dialx.ai
    helm repo update

Step 1: Create a values file​

Start from the upstream Azure simple example and fill in your environment. The required values are:

core:
# Workload Identity managed identity client ID
serviceAccount:
annotations:
azure.workload.identity/client-id: <AZURE_CORE_CLIENT_ID>
env:
aidial.storage.account: <AZURE_CORE_BLOB_STORAGE_NAME>
aidial.storage.endpoint: <AZURE_CORE_BLOB_STORAGE_ENDPOINT>
aidial.identityProviders.azure.jwksUrl: "https://login.microsoftonline.com/<AZURE_TENANT_ID>/discovery/v2.0/keys"
aidial.identityProviders.azure.rolePath: "groups"
aidial.identityProviders.azure.issuerPattern: '^https:\/\/sts\.windows\.net.+$'
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: <CLUSTER_ISSUER>
hosts:
- dial.<DOMAIN>
tls:
- secretName: dial-tls-secret
hosts:
- dial.<DOMAIN>

openai:
env:
aidial.openai.endpoint: <AZURE_MODEL_ENDPOINT>
aidial.openai.key: <AZURE_MODEL_KEY>

chat:
env:
AUTH_CALLBACK_BASE_URL: "https://chat.<DOMAIN>"
AUTH_POST_LOGOUT_REDIRECT_URI: "https://chat.<DOMAIN>"
DIAL_CORE_URL: "http://dial-core.dial.svc.cluster.local"
CORS_ORIGIN: "https://chat.<DOMAIN>"
secrets:
AUTH_SESSION_SECRET: <AUTH_SESSION_SECRET>
AUTH_AZURE_AD_CLIENT_ID: <AUTH_AZURE_AD_CLIENT_ID>
AUTH_AZURE_AD_TENANT_ID: <AZURE_TENANT_ID>
AUTH_AZURE_AD_SECRET: <AUTH_AZURE_AD_SECRET>
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: <CLUSTER_ISSUER>
hosts:
- chat.<DOMAIN>
tls:
- secretName: chat-tls-secret
hosts:
- chat.<DOMAIN>

DIAL Core and DIAL Chat each get their own Ingress-NGINX host and cert-manager-issued certificate, under core.ingress and chat.ingress; see the Azure example - simple for the full values file.

core.env registers Entra ID as the identity provider DIAL Core trusts for user tokens; chat.env/chat.secrets configure DIAL Chat's own sign-in flow against the same app registration. In the app registration, set the redirect URI to match AUTH_CALLBACK_BASE_URL.

Replace the placeholders:

  • <AZURE_CORE_CLIENT_ID> — the Workload Identity managed identity client ID for DIAL Core
  • <AZURE_CORE_BLOB_STORAGE_NAME> — the storage account name
  • <AZURE_CORE_BLOB_STORAGE_ENDPOINT> — the Blob Storage endpoint URL
  • <AZURE_TENANT_ID> — your Microsoft Entra tenant ID
  • <AZURE_MODEL_ENDPOINT> — the Azure OpenAI deployment endpoint
  • <AZURE_MODEL_KEY> — the Azure OpenAI API key
  • <DOMAIN> — your base domain; DIAL Core is exposed at dial.<DOMAIN> and DIAL Chat at chat.<DOMAIN>, for example example.com
  • <AUTH_AZURE_AD_CLIENT_ID> — the app registration's application (client) ID
  • <AUTH_AZURE_AD_SECRET> — the app registration's client secret
  • <AUTH_SESSION_SECRET> — a random secret DIAL Chat uses to encrypt session cookies
  • <CLUSTER_ISSUER> — the cert-manager issuer, for example letsencrypt-production

Generate the DIAL API key, <AUTH_SESSION_SECRET>, and encryption keys before installing. For production, read these from Azure Key Vault rather than inlining them — see Azure Secrets deployment.

Step 2: Install the chart​

helm install dial dial/dial \
-f values.yaml \
--version <CHART_VERSION> \
--namespace dial \
--create-namespace

Pin <CHART_VERSION> to a released chart version.

Step 3: Verify the deployment​

Check that the pods are running:

kubectl get pods -n dial

Once the ingress has an address and both certificates are issued, DIAL Chat is reachable at https://chat.<DOMAIN>/; open it in a browser and sign in through Microsoft Entra ID. Confirm the Unified API responds:

curl https://dial.<DOMAIN>/openai/deployments \
-H "Api-Key: <YOUR_DIAL_API_KEY>"

Result​

DIAL Core, DIAL Chat, Valkey, and the Azure OpenAI adapter run on your AKS cluster, with files persisted to Azure Blob Storage and models served from Azure OpenAI.

Note

The openai alias of the dial chart deploys the standalone OpenAI adapter, which will be deprecated. DIAL Model Provider replaces it. Deploy it with the DIAL Extension chart and move your models as described in Migrate from the standalone adapters.

Production architecture​

The simple example is a starting point. For a production deployment, size the Azure services and configure redundancy as follows.

Azure serviceTier
Azure Kubernetes ServiceStandard, zone-redundant
Ingress-NGINX controllerStandard, fronted by an Azure Load Balancer (Standard SKU)
Storage AccountStandard, Azure Blob Storage, GZRS
Azure Cache for RedisPremium P1, zone-redundant
Azure OpenAISized to capacity requirements; provisioned throughput or pay-as-you-go

Dependencies whose technology may follow corporate standards:

DependencyAzure service
SSOMicrosoft Entra ID
FirewallAzure Application Gateway with WAF
Logging and monitoringAzure Application Insights and Log Analytics
Container registryAzure Container Registry
Secret storageAzure Key Vault

Scalability​

DIAL services are stateless, so they run as multiple concurrent copies. As a baseline, run two copies of each service on different nodes for high availability. The services are not resource-intensive and handle thousands of users without further scaling.

To absorb load spikes, configure autoscaling for DIAL Core based on CPU usage (for example, scale out when usage exceeds 80% of the CPU limit for one minute), and enable cluster autoscaling so new nodes are added when existing nodes lack capacity.

Scaling language models is the more common need, because a single model instance is often capped by request rate limits. With Azure OpenAI there are four levels of scaling, applied in order:

  1. Increase the number of model instances in the same region.
  2. Request a regional quota extension — each region has a fixed tokens-per-minute (TPM) quota per model type.
  3. Create model instances in other regions.
  4. Use multiple subscriptions to access model endpoints.

High availability​

Achieve high availability through zone redundancy: run DIAL service copies in a multi-zone AKS cluster and use highly available configurations for the supporting services.

ServiceAvailability SLAResilient to zone-wide outages
Azure Kubernetes Service99.95% with zone redundancyYes, if configured explicitly
Key Vault99.99%Yes
Storage Account99.99% (read), 99.9% (write)Yes, if configured explicitly
Azure Cache for Redis99.9% with zone redundancyYes, if configured explicitly
Azure OpenAINo SLA—

Because Azure OpenAI has no SLA, define multiple model endpoints in different regions. If one endpoint fails, DIAL automatically tries the next in the list.

Disaster recovery​

DIAL has two parts with separate recovery policies: DIAL Core and the optional DIAL Analytics.

DIAL Core consists of stateless services plus a single permanent store on an Azure Storage account (Valkey is a cache and can be safely restarted). For a region-wide failure:

  • Storage account — with GZRS redundancy it can fail over to the secondary region. After failover it runs as ZRS in one region; set the mode back to GZRS explicitly to restore cross-region failover capability for the next event.
  • All other services — being stateless, redeploy the full set in a secondary region and re-route the DIAL domain name to the new entry point. To meet a tight recovery time objective, keep a passive DIAL Core deployment in the secondary region and activate it on failure.

When the primary region is restored, there is no need to fail back.

DIAL Analytics keeps ingested rows in ClickHouse and its catalog of tables, pipelines, saved queries, and dashboards in PostgreSQL. Back up PostgreSQL to keep those definitions. Replicate the usage-log blobs it reads like any other permanent store: the backfill endpoint can re-ingest a date range from them, up to BACKFILL_MAX_DAYS days per request.

Further reading​