Skip to main content

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.
This template deploys into a GVC you already have — it does not create, provision or manage one. Every workload below runs in each location of that GVC, so the Compactor (a strict one-per-bucket singleton) belongs in a GVC with exactly one location — see Long-Term Storage Tier.

What Gets Created

The MinIO credentials secret is a prerequisite you create yourself; the chart references it by name and never creates, modifies or deletes it.

Prerequisites

None for a default install. Query starts with an empty store list, and you point it at your Store API endpoints with stores (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 access is keyless: the workload identity receives temporary credentials through a Control Plane cloud account, and nothing is stored.
1

Create a bucket

Create an S3 bucket. Set storage.aws.bucket and storage.aws.region to match.
2

Create a bucket-scoped IAM policy

In AWS IAM, create a policy with the JSON below, replacing YOUR_BUCKET with your bucket name:
Set storage.aws.policyName to the policy’s bare name (not its ARN).
3

Set up a cloud account

If you do not have one, create a Cloud Account for your AWS account. Set 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:
Add your 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

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

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

Each 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:
For a Prometheus template install, 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:
  1. The target workload’s internal firewall must allow inbound from this Query workload — set its internalAccess.type to same-org, or to workload-list including //gvc/GVC_NAME/workload/RELEASE_NAME-thanos (this install’s names). A store reported as down by Query is almost always this firewall.
  2. 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.app canonical 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 through cpln 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

The storage tier is off by default. It serves metric history from the bucket your Prometheus sidecars upload blocks to, configured under Object Storage:
  • storeGateway.enabled — Runs the Store Gateway, which serves historical bucket blocks to Query. Query is wired to it automatically, so it needs no stores entry. Its volume set is only a local cache.
  • compactor.enabled — Runs the Compactor, which compacts and downsamples bucket blocks and applies compactor.retention per resolution (raw, fiveMinutes, oneHour). Each value is a duration such as 30d, 2w or 1y, and "0d" keeps data forever; the chart refuses any other form.
Long-term reads need both halves on the same bucket: a Prometheus sidecar uploading blocks to it, and storeGateway.enabled: true here to serve them.
Run exactly one Compactor per bucket, across every install and region. A second Compactor on the same bucket corrupts the block layout and needs manual repair. Enable compactor in one install only, and only in a GVC with a single location — the Compactor workload runs one replica in each location of its GVC.

Object Storage

Set 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

With the default private setup, open a tunnel to the Query workload and browse to http://localhost:10902 for the UI:
With the tunnel open, check readiness and run a PromQL query against everything Query federates:
Point Grafana at Query instead of at individual Prometheus servers to get one deduplicated global view; the Grafana template shows the datasource definition. The Query UI lists every configured store with its health and labels — the first place to look when a source is missing from results.

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:
1

Create the credentials secret

Use the key pair the install already uses — the value of accessKey becomes the accessKey entry, and the value of accessSecret becomes the secretKey entry:
2

Update your values

Delete storage.minio.accessKey and storage.minio.accessSecret, and set storage.minio.credentialsSecretName to the secret’s name.
3

Upgrade the release

The values file must use the chart’s nesting, exactly as the blocks in Configuration show. A flat dotted key is accepted without error and silently changes nothing.

Scaling and Availability

  • Query is stateless and scales horizontally: set replicas to 2 or more for a query tier that keeps answering through rolling restarts and the loss of a replica. replicas is 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 stores list. The instances need no coordination, but only one of them may enable the Compactor.

Troubleshooting

Cause: almost always the target’s internal firewall. A Prometheus sidecar in another GVC does not accept inbound traffic from this Query workload by default.Fix: on the Prometheus install, set 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.
Cause: a 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.
Cause: 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.
Cause: long-term reads need both halves on the same bucket. Either the Prometheus sidecar is not uploading blocks, or the Store Gateway is off or pointed at a different bucket.Fix: enable object storage on the Prometheus install, set storeGateway.enabled: true here, and make sure both name the same bucket and backend.
Cause: on first start it builds a local index cache over every block in the bucket, which is slow on a large bucket.Fix: none needed — wait for it to finish. Raise storeGateway.volumeset.capacity if the cache outgrows the volume.
Cause: after a critical error the Compactor halts — it stays running but stops working, and it is intentionally not restarted for you. Its logs carry the reason:
Fix: resolve the error the log names (see the Compactor documentation), then restart it with 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.
Cause: the dictionary secret named in 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 stores takes effect only through cpln 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-store and RELEASE_NAME-thanos-compact afterwards.

External References

Thanos Documentation

Official Thanos documentation

Query Component

Global query layer, fan-out, and deduplication reference

Store Gateway Component

Serving historical bucket blocks to the query layer

Compactor Component

Compaction, downsampling, and retention reference

Thanos Template

View the source files, default values, and chart definition