Skip to content
PromQL Query Patterns

PromQL Query Patterns Skill

Grafana's official PromQL skill: write, validate, and optimise Prometheus queries, and hunt down the label that blew up your cardinality.

GitHub Docs

Install

Install with the skills CLI

One command for Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf and dozens of other agents. Needs Node.js; the skill is fetched straight from grafana/skills.

$ npx skills add grafana/skills --skill promql

Run it in a terminal. The CLI finds the agents installed on your machine, asks which ones to add the skill to, and whether to install it into the folder you ran it from or for all your projects.

Other ways to install

  1. Open the skill on GitHub and download the folder that holds its SKILL.md.
  2. Put it at ~/.claude/skills/promql/, where Claude Code picks it up in every project.

No scripts or references next to it? Saving the SKILL.md alone as ~/.claude/skills/promql/SKILL.md works too.

Using another agent? Its personal skills folder
Codex
~/.agents/skills/
Cursor
~/.cursor/skills/
GitHub Copilot
~/.copilot/skills/
Gemini CLI
~/.gemini/skills/
Windsurf
~/.codeium/windsurf/skills/
OpenCode
~/.config/opencode/skills/

About

  • rate vs irate vs increase, and the 4x-scrape-interval rule
  • Aggregation with sum, avg, topk, by and without
  • Classic and native histogram_quantile for correct percentiles
  • Ratio queries with divide-by-zero guards
  • SLO and burn-rate maths for error-budget alerts
  • Cardinality-hunting playbook for slow queries and OOMs
  • Recording-rule naming and dashboard-query migration

Prometheus queries fail in quiet ways. A rate() over too short a window returns nothing instead of erroring, a p95 computed from the wrong histogram type is off by an order of magnitude, and a ratio divides by zero the moment traffic drops — none of which announce themselves. This skill from Grafana Labs gives an agent the query patterns that make those failures visible before they reach a dashboard.

What it covers

The core is the set of distinctions that PromQL punishes you for getting wrong: rate versus irate versus increase, label matchers and regex behaviour, and aggregation with sum, avg, topk, by, and without. On top of that it covers classic and native histogram_quantile, ratio expressions with divide-by-zero guards, absent and changes for staleness detection, time offsets and predict_linear for trend work, recording-rule naming conventions, and the SLO and burn-rate arithmetic behind error-budget alerts.

It also carries a cardinality-hunting playbook — the workflow for finding which label is generating the series explosion behind a slow query, an OOMing Prometheus, or an unexpected Grafana Cloud bill.

The rule it enforces first

rate() and increase() need a range vector of at least four scrape intervals. On a 60-second scrape that means [5m] minimum. Most "my query returns no data" reports resolve to this one line, which is why the skill states it as a golden rule up front.

Where it applies

The queries work against Prometheus, Grafana Mimir, and Grafana Cloud Metrics. The skill is written to trigger on the way people actually phrase these requests — "calculate the error rate", "p99 latency", "sum by service", "why is this query slow", "what's filling Mimir" — rather than requiring anyone to say the word PromQL.

Part of the grafana-core plugin in Grafana's public skills marketplace, alongside dashboarding, alerting and IRM, Alloy, Beyla, and OpenTelemetry skills.

Try it

Once installed, ask your agent something like:

Our checkout p95 dashboard looks wrong after we moved to native histograms — rewrite the panel query and add a 2%/1h burn-rate alert for the 99.9% availability SLO.

SKILL.md

The instructions your agent loads, as published by Grafana Labs.

SKILL.md
name
promql
license
Apache-2.0
description
Write, validate, and optimize PromQL for Prometheus / Grafana Mimir / Grafana Cloud Metrics. Covers `rate` vs `irate` vs `increase`, label matchers and regex, `sum / avg / topk / by / without` aggregation, classic + native `histogram_quantile`, ratios with divide-by-zero guards, `absent` / `changes` for staleness, time offsets and `predict_linear`, recording-rule naming, SLO + burn-rate math, and a cardinality-hunting playbook. Use when writing a metric query, fixing wrong p95s, building an error-budget alert, debugging "query is slow", finding the noisy label that blew up cardinality, or migrating a dashboard query to a recording rule — even when the user says "calculate the error rate", "p99 latency", "sum by service", "why is this query slow", or "what's filling Mimir" without naming PromQL.

PromQL Query Patterns

Docs: https://prometheus.io/docs/prometheus/latest/querying/basics/

PromQL returns either an instant vector, a range vector, or a scalar.

Golden rule: rate() / increase() require a range vector ≥ 4× the scrape interval. 60s scrape → use [5m] minimum.

Prerequisites
  • A Prometheus / Mimir / Grafana Cloud endpoint to query (/api/v1/query or via Grafana Explore)
  • The PromQL pattern library in [`references/patterns.md`](references/patterns.md)
Common Workflows
1. Write + validate a query
# 0. Point at your Prometheus/Mimir. For Grafana Cloud, use the metrics endpoint
#    and add basic auth (-u "<metrics_user>:<token>") to each curl below.
PROM=http://localhost:9090   # or https://prometheus-prod-XX.grafana.net/api/prom

# 1. Sketch the query — for "5xx error rate per service":
EXPR='sum(rate(http_requests_total{status_code=~"5.."}[5m])) by (service)'

# 2. Validate syntax + that the metric/labels exist
curl -sG --data-urlencode "query=${EXPR}" \
  "$PROM/api/v1/query" | jq '.status, (.data.result|length)'
# Expect: "success" and result count > 0. If 0 — check label spelling and scrape activity:
curl -sG --data-urlencode "match[]=http_requests_total" "$PROM/api/v1/series" | jq '.data | length'

# 3. Sanity-check the magnitude — open Grafana Explore, paste the expr,
#    confirm the values look right against a known ground truth (k6 run, log count, etc.)
2. Common patterns to copy

Per-status request rate (aggregate AFTER rate):

sum(rate(http_requests_total{job="api"}[5m])) by (status_code)

p95 latency (must keep le in the inner aggregation):

histogram_quantile(0.95,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))

Error rate with divide-by-zero guard:

sum(rate(http_requests_total{status_code=~"5.."}[5m]))
  / (sum(rate(http_requests_total[5m])) > 0)

Full library (recording rules, SLO burn-rate, offsets, cardinality hunt, native histograms): [`references/patterns.md`](references/patterns.md).

3. Convert a slow dashboard query into a recording rule
# 1. Pick the slow expression, give it a recording-rule name
groups:
  - name: http_request_rates
    interval: 1m
    rules:
      - record: job:http_request_duration_p95:rate5m
        expr: |
          histogram_quantile(0.95,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le, job))
# 2. After rules load, verify the new metric exists
curl -sG --data-urlencode "query=job:http_request_duration_p95:rate5m" \
  "$PROM/api/v1/query" | jq '.data.result | length'   # → > 0

# 3. Verify it matches the original expression for at least one sample window
# (Both queries should produce the same value at the same timestamp.)

# 4. Replace the dashboard panel expression with the recording-rule metric.
Common bugs
  • histogram_quantile returns NaN → forgot by (le) in the inner aggregation
  • "No data" → check the metric exists (/api/v1/series) and the window ≥ 4× scrape interval
  • Wrong rate magnitude → counter was aggregated before rate() (always rate() first)
  • Query timeout → series count too high; use topk(...) + a recording rule + drop high-cardinality labels (see [`references/patterns.md`](references/patterns.md))
Resources
Install
Browse all skills →