Agent Observability Platform: Configuration Reference (Azure)
Configuration reference for the Agent Observability data platform on Azure
Optional configuration β TLS, retention, ClickHouse users, evaluation, and sizing.
All settings on this page are optional β the defaults produce a working deployment. Set them as inputs to the Terraform module.
Data retention (TTL)
ClickHouse retains trace data for a fixed window, after which old data is dropped automatically.
| Input | Default | Description |
|---|---|---|
clickhouse_ttl_days | 30 | Days of trace data to retain. Governs the trace tables and their conversation-derived annotation tables. Applied cluster-wide by the schema job, so retention can't drift between replicas. |
ClickHouse users
The platform provisions a least-privilege ClickHouse user model β one SQL user per access path, each scoped to exactly the operations it performs:
| User | Access | Used by |
|---|---|---|
schema_owner | Read/write on the telemetry database, plus DDL (including cluster-wide ON CLUSTER statements) and system-table maintenance | The schema-migration job (creates tables, applies TTLs) and the owner (DEFINER) of the materialized views |
otel | INSERT-only into the raw trace table | The OpenTelemetry Collector. Normalization still works because the materialized views run as schema_owner, not otel |
llm_worker | Read/append on the LLM job-queue tables | The LLM worker |
monte_carlo | Read-only on telemetry, plus append to the job-queue, evaluation-score, and conversation-annotation tables | The Monte Carlo Agent (Trace Exploration, agent monitors, queuing evaluations) β this is the user you connect Monte Carlo as |
probe | system.replicas replication health and SHOW TABLES on the telemetry database (passwordless; no row-level data reads) | The ClickHouse readiness probe β needs no secret or configuration |
readonly_user | SELECT-only (optional) | External SQL clients (e.g. DataGrip), MCP, and the deployment verification script |
admin | Full superuser, including access management (optional, gated) | Break-glass DBA access β reachable only over loopback (pod-exec) by default |
schema_owner, otel, llm_worker, monte_carlo, and probe are always provisioned. readonly_user and admin are opt-in.
Each user's password is generated by the module and stored in Azure Key Vault (RBAC-authorized, purge protection enabled), then synced into the cluster by the External Secrets Operator. Each user has its own secret, exposed as a clickhouse_<user>_credentials_secret_id output. To supply your own passwords instead, set the sensitive clickhouse_passwords input β note the values then also live in your Terraform state. The exception is probe, which is deliberately passwordless β its consumer is the kubelet's readiness check, which can't read a Kubernetes secret β and so has no Key Vault entry; what it can read is limited to replication-health metadata and table names, with no row data exposed.
The stock ClickHouse
defaultsuperuser is removed during deployment as a hardening measure β every client authenticates as one of the users above.
Read-only user
To provision the optional readonly_user (SELECT-only) for external SQL clients:
helm = {
chart_registry = "oci://registry-1.docker.io/montecarlodata"
chart_version = "4.0.0"
clickhouse = {
readonly_user = {
enabled = true
}
}
}Its password's Key Vault secret ID is exposed as the clickhouse_readonly_user_credentials_secret_id output. Remember that external clients reach ClickHouse through the Gateway over HTTPS on port 8443 β configure JDBC/HTTP clients accordingly (the native TCP protocol is not exposed).
Break-glass admin user
The admin superuser is opt-in and, by default, reachable only over loopback (pod-exec) β it is never on a network path. Enable it for break-glass DBA access:
helm = {
chart_registry = "oci://registry-1.docker.io/montecarlodata"
chart_version = "4.0.0"
clickhouse = {
admin = {
enabled = true
}
}
}Its password's Key Vault secret ID is exposed as the clickhouse_admin_credentials_secret_id output (null when disabled).
Resource sizing
Each managed workload exposes optional Kubernetes resource requests/limits via helm.<workload>.resources. Omit a workload (or a requests/limits map) to use the chart defaults β start modest in development and tune up for production.
helm = {
chart_registry = "oci://registry-1.docker.io/montecarlodata"
chart_version = "4.0.0"
clickhouse = {
resources = {
requests = { cpu = "2", memory = "8Gi" }
limits = { cpu = "4", memory = "16Gi" }
}
}
opentelemetry_collector = {
resources = {
requests = { memory = "2Gi" }
limits = { memory = "6Gi" }
}
}
llm_worker = {
resources = {
requests = { cpu = "500m", memory = "1Gi" }
limits = { cpu = "2", memory = "4Gi" }
}
}
}requests and limits are maps keyed by Kubernetes resource name (cpu, memory, ephemeral-storage, etc.); either can be omitted independently.
There are no replica-count overrides for the Collector or the LLM worker on Azure β both run at the chart's defaults. ClickHouse replicas are controlled by clickhouse_replica_count (see High availability below).
Node pools (new-cluster path)
On the new-cluster path, you can size the node pools the workloads run on:
| Input | Default | Description |
|---|---|---|
cluster.vm_size | Standard_D4s_v5 | VM size for the main node pool (Collector, LLM worker, controllers). |
cluster.main_node_pool_size | 2 | Desired/minimum size of the main node pool (set 1 for a cost-optimized single node; max 10). |
cluster.main_node_pool_max_size | 10 | Autoscaler ceiling for the main node pool. |
clickhouse_node_pool.vm_size | Standard_E8s_v5 | VM size for the per-zone ClickHouse node pools. |
keeper_node_pool.vm_size | Standard_D2as_v4 | VM size for the per-zone Keeper node pools (deliberately non-burstable β a throttled quorum voter risks spurious leader elections; avoid B-series). |
keeper_node_pool.storage_size / .storage_class | 10Gi / managed-csi | The Keeper persistent volume (Raft log and snapshots only). |
Node images are pinned by default (node-OS upgrade channel None); roll the stateless pool deliberately by setting or changing node_image_upgrade_token, and upgrade the stateful pools on your own schedule β see High availability β node-image updates. The cluster's Kubernetes version is pinned via kubernetes_version (default "1.35").
High availability
The topology inputs below deploy the standard HA shape β how it works, the dev/testing single-replica variant, and day-2 operations are covered on the High availability page.
| Input | Default | Description |
|---|---|---|
clickhouse_zones | [] | Zone numbers for the per-zone ClickHouse node pools; the list length caps the replica count. Required (non-empty) on the new-cluster path β a single zone is the dev floor; must stay empty on an existing cluster. |
keeper_zones | [] | Zone numbers for the Keeper voter node pools β must be an odd count (3 standard, 1 for dev). Also sets the chart's Keeper replica count. Required (non-empty) on the new-cluster path; must stay empty on an existing cluster. |
clickhouse_replica_count | 1 | ClickHouse replicas (2 for the standard HA shape). Terraform-owned: a chart version bump alone never changes it. |
Storage
ClickHouse data is stored on an Azure managed-disk persistent volume.
| Input | Default | Description |
|---|---|---|
helm.clickhouse.storage_size | 500Gi | Size of the ClickHouse persistent volume. |
clickhouse_storage_class | clickhouse-premium-v2 | StorageClass for the ClickHouse volume. The module creates a dedicated clickhouse-premium-v2 class by default (Premium SSD v2, reclaimPolicy: Retain, volume expansion allowed); set to an existing class name to use your own β the module won't create it, and the PVC stays Pending if it doesn't exist. |
storage_class_clickhouse_premiumv2.iops | 3000 | Provisioned IOPS for the clickhouse-premium-v2 class (min 3000, max 80000; the default is the free baseline). |
storage_class_clickhouse_premiumv2.throughput | 125 | Throughput in MB/s for the clickhouse-premium-v2 class (min 125, max 1200; the default is the free baseline). |
Premium SSD v2 availability varies by region and zone β verify support in your target region and zones before deploying, or point
clickhouse_storage_classat a different class. A StatefulSet'sstorageClassNameis immutable: existing deployments keep their original class, so choose deliberately on the first apply.
Network access
The platform's endpoints sit on a single managed Gateway backed by an internal load balancer β nothing is exposed to the public internet. The controls below govern who can reach what:
| Input | Default | Description |
|---|---|---|
gateway.allowed_source_ranges | null (unrestricted) | Source ranges permitted to reach the Gateway's internal load balancer. null = no restriction; [] = the VNet address space only; a CIDR list = the VNet space plus the listed ranges (the VNet space is always folded in, so a restriction never locks out in-VNet clients β on a bring-your-own VNet, include your address space explicitly). One unioned list, since both endpoints share the Gateway's load balancer. |
cluster.api_server_authorized_ip_ranges | [] | Allow-list for the AKS API server's public endpoint (new-cluster path). Mutually exclusive with private_cluster_enabled. |
cluster.private_cluster_enabled | false | Fully private AKS control plane β no public API endpoint (new-cluster path). Every apply and kubectl then needs a private path: VPN, peering, a private endpoint, or an in-VNet runner. |
cluster.local_account_disabled | true | Disables the legacy certificate-based cluster admin account β all access goes through Entra ID and Azure RBAC. Set false only to pre-stage a break-glass kubeconfig for an identity-provider outage. |
The Gateway source ranges are the primary network control for reaching ClickHouse β the network-facing ClickHouse users accept connections from any source at the ClickHouse layer. This includes the passwordless
probeuser (metadata-only access to replication health and table names) β one more reason to restrictgateway.allowed_source_ranges. (Theadminsuperuser is the exception: it is loopback-only and never reachable through the Gateway.) Leaving the default unrestricted means anyone who can route to the internal load balancer can attempt to connect. Set it before enablingreadonly_userfor external SQL clients.
Two further network facts worth knowing:
- Egress is a NAT gateway. Module-created VNets route outbound traffic through a NAT gateway with a static public IP, so pulls from Docker Hub, Let's Encrypt validation, and Foundry calls present one stable source address.
- etcd is encrypted with your key. New clusters enable AKS KMS etcd encryption with a customer-managed key in the module's Key Vault.
TLS
TLS is enabled by default and terminates on the managed Gateway. cert-manager issues publicly-trusted Let's Encrypt certificates for your otel_collector_domain and clickhouse_domain via DNS-01 validation against dns_zone, and renews them automatically β there are no certificate resources to manage or rotate yourself. Behind the Gateway, traffic is re-encrypted to the Collector's and ClickHouse's in-cluster certificates (issued by cert-manager, validated via trust-manager), so it is encrypted end to end.
| Input | Default | Description |
|---|---|---|
gateway.letsencrypt_email | null | Optional ACME contact address for the Let's Encrypt account. Omit for a contactless account β certificates still auto-renew. |
The Gateway is HTTP-family (L7) only: telemetry is ingested as OTLP over HTTP (not gRPC), and ClickHouse is served over its HTTPS interface on port 8443 β the native TCP protocol is not exposed. "Publicly trusted" describes the certificate chain; the endpoints stay on a private IP.
Evaluation (Microsoft Foundry)
The LLM worker performs evaluation by calling Claude on a Microsoft Foundry resource the module provisions β an AIServices account, a project, and one deployment per curated Claude model, with a custom least-privilege inference role granted to the worker's Workload Identity. Authentication is Entra-only (disableLocalAuth = true β no API keys anywhere), over the account's public endpoint.
| Input | Default | Description |
|---|---|---|
helm.llm_worker.foundry.organization_name | β (required) | Your organization's name β the Azure Marketplace attestation for the Anthropic pay-per-token offer. |
helm.llm_worker.foundry.account_name | "<cluster name>-foundry" | The Foundry account name β also its globally unique subdomain (<name>.services.ai.azure.com). Set a distinct value per deployment, or a name collision fails the apply. |
helm.llm_worker.foundry.location | the module's location | Azure region for the Foundry account and its model deployments. Set it when Claude on Foundry is offered in a region other than the one hosting the cluster. |
helm.llm_worker.foundry.country_code / .industry | "US" / "technology" | Attestation fields recorded with the Marketplace offer (industry is lowercase). |
helm.llm_worker.foundry.default_capacity | 100 | Capacity per model deployment, in units of 1,000 tokens per minute (100 = 100,000 TPM, GlobalStandard). Lower it if the default exceeds the Claude quota granted in your subscription. |
helm.llm_worker.foundry.deployments | haiku / sonnet / opus | The curated Claude model deployments β defaults: claude-haiku-4-5, claude-sonnet-5, claude-opus-4-8. Each entry supports name (the Claude model ID, which doubles as the deployment name), optional version, and optional per-deployment capacity. |
helm.llm_worker.image_repository | montecarlodata/ao-llm-worker | Override the LLM-worker image repository. |
helm.llm_worker.image_tag | latest-azure | LLM-worker image tag. latest-azure is a floating tag β pin to a published version for production; the current release is 1.1.0-azure. |
Claude model availability and tokens-per-minute quota vary by region and subscription β verify both for the Foundry region (helm.llm_worker.foundry.location, defaulting to your location) in the Azure portal before relying on the defaults, and check current model IDs there if a deployment fails to create.
The account name, account ID, project name, and project ID are exposed as the foundry_account_name, foundry_account_id, foundry_project_name, and foundry_project_id outputs.
Ingestion into the platform is OTLP push only β there is no object-storage (blob) ingest path on Azure, and the Collector runs with no cloud identity at all.
Updated 1 day ago
