Agent Observability Platform: Configuration Reference (AWS)

Optional configuration on AWS β€” TLS, retention, ClickHouse users, S3 ingest, and sizing

☁️

This page covers AWS. Also available for Azure and GCP.

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.

InputDefaultDescription
clickhouse_ttl_days30Days 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:

UserAccessUsed by
schema_ownerRead/write on the telemetry database, plus DDL (including cluster-wide ON CLUSTER statements) and system-table maintenanceThe schema-migration job (creates tables, applies TTLs) and the owner (DEFINER) of the materialized views
otelAppend into the telemetry tablesThe OpenTelemetry Collector. Can be tightened to INSERT-only with restrict_grants (see below)
llm_workerRead/append on the LLM job-queue tablesThe LLM worker
monte_carloRead-only on telemetry, plus append to the job-queue, evaluation-score, and conversation-annotation tablesThe Monte Carlo Agent (Trace Exploration, agent monitors, queuing evaluations) β€” this is the user you connect Monte Carlo as
probesystem.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_userSELECT-only (optional)External SQL clients (e.g. DataGrip), MCP, and the deployment verification script
adminFull 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 AWS Secrets Manager (KMS-encrypted), then synced into the cluster by the External Secrets Operator. Each user has its own secret, exposed as a clickhouse_<user>_credentials_secret_arn output. 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 Secrets Manager entry. Like the other non-admin users, probe is network-facing rather than loopback-restricted; what it can read is limited to replication-health metadata and table names, with no row data exposed. clickhouse_nlb_allowed_source_ranges (see Network access) governs who can reach it.

πŸ“˜

The stock ClickHouse default superuser 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, profile readonly_settings with readonly = 2 β€” SELECT plus per-session SET, so JDBC clients such as DataGrip connect cleanly):

helm = {
  chart_registry = "oci://registry-1.docker.io/montecarlodata"
  chart_version  = "4.0.0"

  clickhouse = {
    readonly_user = {
      enabled = true
      # password = "..."  # optional; omit to auto-generate
    }
  }
}

Its password ARN is exposed as the clickhouse_readonly_user_credentials_secret_arn output.

πŸ“˜

The module requires ao-data-platform chart version >= 2.0.0, which provisions the full least-privilege user model (including readonly_user).

Ingest user lock-down

otel defaults to read/write so the migration to the monte_carlo user is non-breaking. After Monte Carlo connects as monte_carlo (see Connect to Monte Carlo), set clickhouse.otel.restrict_grants = true to tighten otel to INSERT-only on the raw trace table (otel_traces.otel_traces). Normalization keeps working because the materialized views run as schema_owner, not otel.

πŸ“˜

Transitional. restrict_grants is a migration aid and will be removed once otel is INSERT-only by default.

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
      # password = "..."  # optional; omit to auto-generate
    }
  }
}

Its password ARN is exposed as the clickhouse_admin_credentials_secret_arn output (null when disabled). Requires chart version >= 2.0.0.

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.

Node groups (new-cluster path)

On the new-cluster path, you can size the node groups the workloads run on:

InputDefaultDescription
clickhouse_ha_node_group.instance_typer6i.xlargeInstance type for the per-AZ ClickHouse node groups.
keeper_node_group.instance_typem6i.largeInstance type for the per-AZ Keeper node groups (deliberately non-burstable β€” a throttled quorum voter risks spurious leader elections).
keeper_node_group.storage_size / .storage_class10Gi / gp3The Keeper persistent volume (Raft log and snapshots only).
clickhouse_node_group.instance_typer5.xlargeInstance type for the legacy single-instance ClickHouse node group.
cluster.node_instance_typet3.largeInstance type for the main node group (Collector, LLM worker, controllers).
cluster.main_node_group_size2Desired/minimum size of the main node group (set 1 for a cost-optimized single node; max 10).

The ClickHouse and Keeper node groups pin their AMI version by default; update it deliberately via each node group's ami_release_version β€” see High availability β€” node AMI updates.

Workload replicas

InputDefaultDescription
helm.opentelemetry_collector.replica_countnull (chart default)OpenTelemetry Collector replicas. 0 is not honored β€” the chart treats it as unset; to stop ingest, pause the senders upstream instead.
helm.llm_worker.replica_countnull (chart default)LLM-worker replicas β€” 0 or 1 only. 0 pauses the worker declaratively (survives applies); set back to resume.

ClickHouse replicas are controlled by clickhouse_replica_count β€” see High availability below.

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.

InputDefaultDescription
clickhouse_availability_zones[]AZ names for the per-AZ ClickHouse node groups; the list length caps the replica count.
keeper_availability_zones[]AZ names for the Keeper voter node groups β€” must be an odd count (3 standard, 1 for dev). Also sets the chart's Keeper replica count.
clickhouse_replica_count1ClickHouse replicas (2 for the standard HA shape). Terraform-owned: a chart version bump alone never changes it.
enforce_clickhouse_volume_az_matchtrueMigration aid β€” requires clickhouse_availability_zones[0] to match the AZ of any existing single-instance ClickHouse volume. Most users leave the default.
manage_legacy_clickhouse_node_grouptrueMigration aid β€” keeps the legacy single-instance ClickHouse node group under module management. Most users leave the default.

Storage

ClickHouse data is stored on an EBS-backed persistent volume.

InputDefaultDescription
helm.clickhouse.storage_size500GiSize of the ClickHouse persistent volume.
clickhouse_storage_classclickhouse-gp3StorageClass for the ClickHouse volume. The module creates a dedicated clickhouse-gp3 class by default; set to an existing class name to use your own.
storage_class_clickhouse_gp3.iops3000Provisioned IOPS for the clickhouse-gp3 class (min 3000, max 16000).
storage_class_clickhouse_gp3.throughput125Throughput in MB/s for the clickhouse-gp3 class (min 125, max 1000; must be ≀ iops Γ— 0.25).

Network access

By default the ClickHouse and OpenTelemetry Collector Network Load Balancers are internal, but accept traffic from any source that can route to them. Restrict the source ranges to harden access:

InputDefaultDescription
clickhouse_nlb_allowed_source_rangesnull (unrestricted)CIDR ranges permitted to reach the ClickHouse NLB. [] restricts to the VPC.
otel_collector_nlb_allowed_source_rangesnull (unrestricted)CIDR ranges permitted to reach the OpenTelemetry Collector NLB. [] restricts to the VPC.
networking.control_plane_subnet_ids[]Existing-cluster only: pins the EKS control-plane subnets when widening existing_private_subnet_ids into a new AZ (a cluster's control-plane AZ set is immutable). See High availability β€” existing-cluster deployments.

Both NLBs run with cross-zone routing enabled and their subnet placement pinned to the module's private subnets (one subnet per AZ) β€” see Harden network access.

πŸ”’

These NLB 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 probe user (metadata-only access to replication health and table names) β€” one more reason to restrict clickhouse_nlb_allowed_source_ranges. (The admin superuser is the exception: it is loopback-only and never reachable over the NLB.) Leaving the default unrestricted means anyone who can route to the internal NLB can attempt to connect. Set clickhouse_nlb_allowed_source_ranges to your VPC or a specific CIDR list, especially before enabling readonly_user for external SQL clients.

TLS

TLS is enabled by default. The OpenTelemetry Collector and ClickHouse NLB endpoints are TLS-terminated using ACM certificates that the module provisions for your otel_collector_domain and clickhouse_domain. Traffic between the Collector and ClickHouse inside the cluster is secured with certificates issued by cert-manager.

Encryption at rest

All persistent data is encrypted at rest by default:

  • ClickHouse trace data β€” the module's gp3 and clickhouse-gp3 StorageClasses set encrypted = "true", so the EBS volumes backing ClickHouse and Keeper are encrypted.
  • Credentials β€” every ClickHouse-user secret in AWS Secrets Manager is encrypted with a customer-managed KMS key the module creates (aws_kms_key.pipeline_secrets).
  • Kubernetes secrets β€” EKS envelope encryption of etcd secrets is enabled by default on the new-cluster path.

Bring-your-own-key is not exposed for the Secrets Manager or EKS keys (both are module-created). The optional S3 trace-ingest buckets are customer-owned resources, so their encryption at rest is governed by your own bucket configuration.

S3 ingest (OTel Collector awss3 receivers)

In addition to its OTLP endpoints, the OpenTelemetry Collector can ingest OTLP traces from objects in one or more S3 buckets, each notified via its own SQS queue. Set helm.opentelemetry_collector.awss3_receivers with one map entry per queue/bucket pair (requires module >= 2.2.0). For every enabled entry, the module:

  • Renders an awss3 receiver with component ID awss3/<key>, configured with the supplied SQS URL/region and S3 bucket/region/prefix, and appends it to the trace pipeline so the receiver is actually wired up.
  • Attaches an inline policy to the otel-collector IRSA role granting sqs:ReceiveMessage/DeleteMessage/GetQueueAttributes/GetQueueUrl on every receiver's queue and s3:GetObject/GetBucketLocation on every receiver's bucket.
helm = {
  chart_registry = "oci://registry-1.docker.io/montecarlodata"
  chart_version  = "4.0.0"

  opentelemetry_collector = {
    awss3_receivers = {
      traces = {
        sqs_queue_arn = "arn:aws:sqs:us-east-1:123456789012:otel-traces"
        sqs_queue_url = "https://sqs.us-east-1.amazonaws.com/123456789012/otel-traces"
        s3_bucket     = "acme-otel-traces"
        # enabled defaults to true
        # sqs_region / s3_region default to var.region
        # s3_prefix defaults to ""
      }
    }
  }
}
⚠️

Give each receiver its own dedicated SQS queue β€” never share a queue. In SQS mode a receiver fetches whatever object each notification names but filters the records against its own configured bucket/prefix, and it deletes messages whose records were all filtered out β€” so two receivers sharing one queue destroy each other's notifications. Duplicate sqs_queue_arn values across enabled receivers are rejected at plan time. To feed several consumers from one bucket's events, fan the bucket notification out via SNS with a queue per consumer.

Per entry: enabled defaults to true (set false to keep the entry without rendering it); sqs_region and s3_region default to var.region; s3_prefix defaults to "" (any object in the bucket) and is normalized internally to include a single trailing / when non-empty (so "traces" and "traces/" behave identically).

Adding, changing, or removing a receiver changes the collector's rendered config, which rolling-restarts the collector Deployment on apply. (helm.opentelemetry_collector.awss3_receiver β€” singular β€” is the deprecated single-receiver form; it keeps working throughout module v2.x but use awss3_receivers for new configuration.)

Evaluation (Amazon Bedrock)

On AWS deployments the LLM worker invokes models through Amazon Bedrock. By default it targets your deployment region.

InputDefaultDescription
helm.llm_worker.bedrock_regionvar.regionAWS region for Bedrock API calls.
helm.llm_worker.image_repositoryderived from chart_registry (e.g. registry-1.docker.io/montecarlodata/ao-llm-worker)Override the LLM-worker image repository.
helm.llm_worker.image_taglatest-awsLLM-worker image tag. The worker image is published with per-cloud tags; latest-aws is a floating tag. Pin a released tag for production β€” the current release is 1.1.0-aws.

Did this page help you?