Thanos query
Claude Code skill for CNCF Thanos, Prometheus, PromQL, and Kubernetes observability
npx -y skills add Yusin0903/thanos-skills --skill thanos-queryAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Claude Code skill for querying CNCF Thanos HTTP API and cross-region Prometheus metrics via PromQL. Use when running PromQL/MetricsQL queries against a Thanos Querier or Query Frontend, discovering metrics/labels across regions, inspecting Thanos stores (sidecars/store-gateway), checking alerts/rules, troubleshooting empty query results, or analyzing observability data across multiple Prometheus instances. Triggers on: thanos query, promql, cross-region metrics, p50/p95/p99 latency, sidecar status, store-gateway, query frontend, label discovery across regions, empty results, missing data, data delay, no data found, query returns empty, Kubernetes observability, Prometheus skill, Thanos skill.
SKILL.md
11.6 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
Thanos Query
Query Thanos HTTP API directly via curl. Thanos implements the full Prometheus HTTP v1 API plus Thanos-specific endpoints (/api/v1/stores, dedup/partial_response params). Covers instant/range queries, label/series discovery, stores topology, alerts, and rules across multiple Prometheus backends.
Environment
# $THANOS_METRICS_URL — base URL of Thanos Querier or Query Frontend HTTP API
# Required. Set in ~/.claude/settings.json under `env`.
# Example: http://thanos-query.example.internal:10902
# https://thanos.example.com
#
# $THANOS_AUTH_HEADER — optional full HTTP header line, if your deployment fronts
# Thanos with a reverse proxy that requires auth (basic/bearer).
# Example: Authorization: Bearer <token>
# Authorization: Basic <base64(user:pass)>
# Leave empty/unset when Thanos is reached directly (Thanos has no built-in auth).
Auth pattern
All curl commands use conditional auth — works for both reverse-proxied and direct deployments:
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/query?query=up" | jq .
When THANOS_AUTH_HEADER is empty, the -H flag is omitted automatically.
Core endpoints
Instant query
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=up' \
"$THANOS_METRICS_URL/api/v1/query" | jq .
# Query at a specific time
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=up' \
--data-urlencode 'time=2026-03-07T09:00:00Z' \
"$THANOS_METRICS_URL/api/v1/query" | jq .
Params: query (required), time (RFC3339 or unix seconds), timeout, dedup, partial_response, max_source_resolution.
Range query
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=rate(http_requests_total[5m])' \
--data-urlencode 'start=2026-03-07T00:00:00Z' \
--data-urlencode 'end=2026-03-07T12:00:00Z' \
--data-urlencode 'step=5m' \
"$THANOS_METRICS_URL/api/v1/query_range" | jq .
Params: query, start, end, step (required); plus timeout, dedup, partial_response, max_source_resolution.
Label and series discovery
# All label names
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/labels" | jq '.data[]'
# Values of one label
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/label/region/values" | jq '.data[]'
# Find series matching a selector
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'match[]={job="api-server"}' \
"$THANOS_METRICS_URL/api/v1/series?limit=20" | jq '.data'
Thanos-specific: stores topology
# List all backing stores (sidecars, store-gateway, ruler, receive)
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/stores" | jq '.data | to_entries[] | {kind: .key, count: (.value | length)}'
# Detail: which stores per region, last-error state
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/stores" \
| jq '.data | to_entries[] | .key as $k | .value[] | {kind:$k, name, lastError, labelSets}'
Alerts and rules
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/alerts" | jq '.data.alerts[]'
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/rules" | jq '.data.groups[]'
Cross-region queries
Thanos exposes each backend Prometheus's external_labels on every metric. Common label conventions:
regionorthanos_region— the geographic / logical regioncluster— K8s cluster nameenvironment— env tier (dev/staging/prod)replica— HA Prometheus replica (deduped by querier)
Use these to scope queries:
# Filter by region label
--data-urlencode 'query=up{region="us-west"}'
# Group by region for cross-region comparison
--data-urlencode 'query=count(up) by (region)'
# P95 latency per region per HTTP verb
--data-urlencode 'query=histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, region, verb))'
Inspect which label names your deployment actually uses via /api/v1/labels first — the conventions above are common but not universal.
Thanos-specific query params
These extend the standard Prometheus query API:
| Param | Default | Use |
|---|---|---|
dedup | true | Dedup HA Prometheus pair series via replica label |
partial_response | depends on flag | Return what's available even if some store is down (useful for cross-region availability) |
max_source_resolution | 0s | Use downsampled blocks: 0s raw, 5m, 1h, or auto |
replicaLabels | from CLI | Override the replica label for this query |
storeMatch[] | none | Restrict to specific stores: {__address__=~"prom-foo.*"} |
engine | thanos | PromQL engine: prometheus or thanos |
Example — long range query against downsampled data:
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=avg_over_time(up[30d])' \
--data-urlencode 'max_source_resolution=1h' \
"$THANOS_METRICS_URL/api/v1/query" | jq .
Common patterns
# Service health across all regions
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=count(up == 0) by (region, job)' \
"$THANOS_METRICS_URL/api/v1/query" | jq '.data.result'
# Error rate across regions (assumes standard label `code`)
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=sum(rate(http_requests_total{code=~"5.."}[5m])) by (region, job)' \
"$THANOS_METRICS_URL/api/v1/query" | jq '.data.result'
# P99 latency (histogram) — last 5 minutes per region
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, region))' \
"$THANOS_METRICS_URL/api/v1/query" | jq '.data.result'
Troubleshooting: empty query results
When an instant query or rate() query returns [], do NOT iteratively widen the time window in small steps (5m → 10m → 30m → 1h). That wastes time and obscures the real cause. Follow this checklist instead:
Empty result triage:
- [ ] Step 1: Check stores topology — is there a sidecar for that region?
- [ ] Step 2: Verify the metric name exists at all
- [ ] Step 3: Verify the label value (label names vary across deployments)
- [ ] Step 4: Run a wide range query (6h+) to find the last data point
- [ ] Step 5: Only narrow down once you have an upper bound on the gap
Step 1 — Stores topology first (architecture matters)
Before assuming the query is wrong, check what backs each region:
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
"$THANOS_METRICS_URL/api/v1/stores" \
| jq '.data | to_entries[] | .key as $k | .value[] | {kind:$k, name, lastError, labelSets}'
Interpret the result:
| What you see for that region | What it means |
|---|---|
Has a sidecar entry, lastError: null | Real-time data available — empty result is a query problem |
Has a sidecar entry, lastError populated | Sidecar is broken — that's your answer |
Only store (store-gateway), no sidecar | Historical data only. Expect ~2h upload delay. rate() over last 5m will almost always be empty. |
| Neither sidecar nor store for that region | Region not wired into this Thanos — query the right Querier |
This single check resolves most "empty result" mysteries. Do it before anything else.
Step 2 — Does the metric exist?
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'match[]={__name__=~".*duration.*bucket"}' \
"$THANOS_METRICS_URL/api/v1/series?limit=5" \
| jq '[.data[] | .__name__] | unique'
If the metric name is wrong (e.g. http_request_duration_seconds_bucket vs api_request_duration_milliseconds_bucket), nothing else matters. Confirm the actual name first.
Step 3 — Do the label values match?
Don't assume environment="stg" is the right scope. Inspect actual labels on the metric:
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'match[]={__name__="<metric_name>"}' \
"$THANOS_METRICS_URL/api/v1/series?limit=1" | jq '.data[0]'
Look for region, thanos_region, cluster, environment — different deployments scope differently.
Step 4 — Wide range query to find the gap
Once topology and naming are confirmed, find the last data point with one wide query, not many narrow ones:
# 6 hours is the default — go wider if needed. NEVER start with 30 minutes.
NOW=$(date -u +%s)
curl -s ${THANOS_AUTH_HEADER:+-H} ${THANOS_AUTH_HEADER:+"$THANOS_AUTH_HEADER"} \
--data-urlencode 'query=count(<metric>{<labels>})' \
--data-urlencode "start=$(( NOW - 21600 ))" \
--data-urlencode "end=$NOW" \
--data-urlencode 'step=1m' \
"$THANOS_METRICS_URL/api/v1/query_range" \
| jq '.data.result[0].values | .[-5:] | map({time: (.[0] | todate), count: .[1]})'
If still empty, double the window (12h, 24h, 3d). Cheap operation — go wide and bisect, don't crawl forward.
Step 5 — Now narrow down
Only after you know roughly when data stopped, query the surrounding window with step=1m to pinpoint the exact cutoff.
Anti-patterns
- ❌ Repeatedly retrying
rate(...[5m])with no data —rate()needs ≥2 samples in the window; if scrape stopped, no time window will help. - ❌ Iteratively expanding
[5m] → [10m] → [30m]hoping data appears. - ❌ Querying
/api/v1/query(instant) after/api/v1/query_rangeshowed data — staleness markers may hide instant results even when range queries succeed. - ❌ Assuming the metric/label is wrong before checking
/api/v1/stores.
Notes
- POST endpoints accept
application/x-www-form-urlencoded; use--data-urlencodefor queries with special chars. match[]requires the[]suffix.- Label values endpoint takes label name as path parameter:
/api/v1/label/{name}/values. - Response shape is identical to Prometheus HTTP v1 API for
/query,/query_range,/labels,/series,/alerts,/rules./storesis Thanos-specific. - Thanos has no built-in authentication. If your deployment is behind a reverse proxy, set
THANOS_AUTH_HEADERaccordingly. - See REFERENCE.md for full endpoint reference and parameters.
- See EXAMPLES.md for end-to-end query examples.
Gives 0 of the 12 instructions most monitoring observability skills give in ~3.2k tokens
Counted across 481 of the 483 authors here whose files we hold, read 2026-08-06
- link every alert to a runbookin 43 of 481, across 35 files
- use structured json loggingin 36 of 481, across 31 files
- alert on user-facing symptomsin 20 of 481, across 15 files
- emit structured JSON logs with stable event namesin 18 of 481, across 13 files
- propagate trace context across boundariesin 16 of 481
- use histograms for latency trackingin 14 of 481, across 9 files
- use OpenTelemetry for distributed tracingin 13 of 481, across 8 files
- include a correlation ID on every log linein 13 of 481, across 8 files
- Define service level objectivesin 10 of 481, across 7 files
- Call useAzureMonitor before importing other modulesin 9 of 481, across 2 files
- stop and ask for clarification if inputs are missingin 9 of 481, across 2 files
- define on-call questions before adding telemetryin 9 of 481, across 4 files
Said here and by no other author read
- query the thanos http api directly via curl
- use conditional auth header in all curl commands
- scope queries using external labels for cross-region comparison
- check stores topology first when troubleshooting empty results
- verify the metric name exists before debugging further
- verify actual label values match the queried selector
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.