Overview
Thanos turns many independent Prometheus servers into one queryable metrics system. This template deploys Thanos Query — a stateless global PromQL layer that fans each query out over the gRPC Store API endpoints you list (typically the Thanos sidecar of each Prometheus template install, in any GVC or region) and deduplicates HA pairs — plus an optional object-storage tier (Store Gateway and Compactor) that serves and maintains long-term metric history in your bucket. This is a self-hosted query layer for your own metrics from your own Prometheus servers. It is separate from — and not a replacement for — Control Plane’s built-in observability, which keeps collecting and dashboarding your workloads’ metrics natively.What Gets Created
Prerequisites
None for a default install. Query starts with an empty store list, and you point it at your Store API endpoints withstores (see Store Endpoints).
The optional storage tier (storeGateway.enabled / compactor.enabled) needs an existing bucket in one of the supported backends — the same bucket your Prometheus template’s Thanos sidecar uploads blocks to. Complete the steps for your chosen backend, then set storage.type to match.
- AWS S3
- Google Cloud Storage
- S3-compatible (MinIO, R2, Wasabi)
Create a bucket
storage.aws.bucket and storage.aws.region to match.Create a bucket-scoped IAM policy
YOUR_BUCKET with your bucket name:storage.aws.policyName to the policy’s bare name (not its ARN).Set up a cloud account
storage.aws.cloudAccountName to its name, and storage.type to aws (the default).Installation
A default install needs no values — it deploys Query alone, into your GVC:stores list (and, for long-term history, the storage tier) in a values file passed with -f, either at install time or later with cpln helm upgrade.
UI
CLI
Terraform
Pulumi
Configuration
Query
image is shared by all three components. resources sizes the Query container; the Store Gateway and Compactor have their own resources blocks under their keys. Query is stateless, so replicas of 2 or more needs no coordination and gives an HA query tier — see Scaling and Availability.
Store Endpoints
stores entry is a gRPC Store API endpoint — typically the Thanos sidecar of a Prometheus template install — written as host:port with no scheme. The chart refuses to render an entry carrying a scheme or lacking a port. Address every endpoint by its fully qualified service name, whether it lives in the same GVC, another GVC or another region:
RELEASE_NAME and GVC_NAME are that install’s own release and GVC; the sidecar serves the Store API on 10901. Two requirements apply to endpoints in another GVC, both on the Prometheus side:
- The target workload’s internal firewall must allow inbound from this Query workload — set its
internalAccess.typetosame-org, or toworkload-listincluding//gvc/GVC_NAME/workload/RELEASE_NAME-thanos(this install’s names). A store reported as down by Query is almost always this firewall. - Internal traffic between locations (Query in one region, sidecars in another) incurs egress charges — co-locate the query tier with its stores where practical.
queryReplicaLabels names the external label (or labels) that mark HA duplicates: series identical except for this label are collapsed into one at query time. The label name must exactly match the external label your HA Prometheus pair sets through the Prometheus template’s externalLabels (for example replica: a and replica: b).
Endpoints are passed to Query as arguments, so changing stores takes effect through cpln helm upgrade, which redeploys Query with the new list. Query keeps answering while some listed endpoints are down.
Access
publicAccess.enabled— Serves the Query UI and PromQL API on a*.cpln.appcanonical endpoint. Query has no built-in authentication: anyone with the URL can run queries against everything it federates. Keep it off (the default) and reach the UI throughcpln port-forward(see Connecting), or put your own authenticating proxy in front.internalAccess.type— Internal firewall scope of the Query workload:
internalAccess.workloads entries take the form //gvc/GVC_NAME/workload/WORKLOAD_NAME. Allow several minutes after changing an access setting before concluding it did not apply.
Long-Term Storage Tier
storeGateway.enabled— Runs the Store Gateway, which serves historical bucket blocks to Query. Query is wired to it automatically, so it needs nostoresentry. Its volume set is only a local cache.compactor.enabled— Runs the Compactor, which compacts and downsamples bucket blocks and appliescompactor.retentionper resolution (raw,fiveMinutes,oneHour). Each value is a duration such as30d,2wor1y, and"0d"keeps data forever; the chart refuses any other form.
storeGateway.enabled: true here to serve them.
Object Storage
storage.type to aws, gcp or minio and fill in that block; the others are ignored. Nothing here is read unless storeGateway.enabled or compactor.enabled is true. The setup for each backend is under Prerequisites.
Connecting
http://localhost:10902 for the UI:
Operations
Upgrading from 1.0.0
Version 1.1.0 renamed the resource limits in all three resource blocks:resources.cpu and resources.memory became resources.maxCpu and resources.maxMemory, and the same rename applies under storeGateway.resources and compactor.resources. The old keys are not rejected — they are silently ignored, so a values file still carrying them installs with the default limits. Rename them before you upgrade. The MinIO change under Upgrading from 1.1.0 also applies.
Upgrading from 1.1.0
Version 1.2.0 moved the MinIO object-storage credentials out of Helm values.storage.minio.accessKey and storage.minio.accessSecret were removed and replaced by storage.minio.credentialsSecretName, which names a dictionary secret you create. Installs using storage.type: aws or gcp are unaffected. With storage.type: minio, carrying the old keys stops the upgrade at render, before anything is applied:
Create the credentials secret
accessKey becomes the accessKey entry, and the value of accessSecret becomes the secretKey entry:Update your values
storage.minio.accessKey and storage.minio.accessSecret, and set storage.minio.credentialsSecretName to the secret’s name.Upgrade the release
Scaling and Availability
- Query is stateless and scales horizontally: set
replicasto2or more for a query tier that keeps answering through rolling restarts and the loss of a replica.replicasis the fixed instance count in each location of the GVC. - Store Gateway runs as one instance. Its volume set is a cache: losing it costs a rebuild on the next start, never data.
- Compactor can never scale — it is a strict singleton per bucket. Its replica count is fixed at one and is not a value.
- For a query tier in several regions, install this template once per regional GVC, each with the same
storeslist. The instances need no coordination, but only one of them may enable the Compactor.
Troubleshooting
A store is reported as down in the Query UI
A store is reported as down in the Query UI
internalAccess.type to same-org, or to workload-list including //gvc/GVC_NAME/workload/RELEASE_NAME-thanos, and upgrade that release. Allow several minutes for the change to apply. Also confirm the entry is the fully qualified WORKLOAD_NAME.GVC_NAME.cpln.local:10901 form.Install or upgrade fails with stores entries must be host port
Install or upgrade fails with stores entries must be host port
stores entry carries a scheme or has no port. The chart stops at render with thanos: stores entries must be host:port with NO scheme or thanos: stores entries must be host:port (gRPC Store API, typically :10901).Fix: write each entry as host:port only — for example RELEASE_NAME-prometheus.GVC_NAME.cpln.local:10901, with no http:// or grpc:// prefix.Duplicate series from an HA Prometheus pair
Duplicate series from an HA Prometheus pair
queryReplicaLabels does not match the external label the pair sets, so Query sees two distinct series.Fix: set queryReplicaLabels to exactly the label name used in the pair’s externalLabels (replica by default), and upgrade.Older data is missing from query results
Older data is missing from query results
storeGateway.enabled: true here, and make sure both name the same bucket and backend.The Store Gateway takes a long time to become ready
The Store Gateway takes a long time to become ready
storeGateway.volumeset.capacity if the cache outgrows the volume.The Compactor is running but bucket blocks are not being compacted
The Compactor is running but bucket blocks are not being compacted
cpln workload force-redeployment RELEASE_NAME-thanos-compact --gvc GVC_NAME. If more than one Compactor has ever run against the bucket, stop the extra one first.Store Gateway or Compactor never starts with storage.type minio
Store Gateway or Compactor never starts with storage.type minio
storage.minio.credentialsSecretName does not exist, so the deployment waits on it with no containers and no logs.Fix: read status.versions[].message with cpln workload get-deployments RELEASE_NAME-thanos-store --gvc GVC_NAME -o yaml, create the named secret as under Prerequisites, and wait for recovery or force a redeployment.Important Notes
- Query has no built-in authentication — keep
publicAccess.enabled: false, or front Query with your own authenticating proxy. - Run exactly one Compactor per bucket, across every install and region, and only in a single-location GVC — a second one corrupts the block layout.
- Cross-GVC stores need a firewall change on the Prometheus side — see Store Endpoints.
- Changing
storestakes effect only throughcpln helm upgrade. - Disabling the storage tier does not remove existing cloud access from the identity — a previously applied AWS or GCP binding stays until you edit the identity, or uninstall and reinstall the release.
- Rotating the MinIO credentials secret is not picked up by running workloads — force a redeployment of
RELEASE_NAME-thanos-storeandRELEASE_NAME-thanos-compactafterwards.