PromQL cheatsheet
One-pager
Reference · v2.x

Everything you need to write PromQL, on one page.

A modern reference for the Prometheus query language — types, selectors, operators, aggregations, functions, time syntax, and the gotchas you only learn at 3am. Beginner sections first, advanced as you scroll.

Data types

What every PromQL expression evaluates to

Instant vector

basic

A set of time series, each with a single sample at the evaluation timestamp. The most common return type.

http_requests_total
# → one value per matching series, "now"

Range vector

basic

Each series carries a window of samples. You can't graph one directly — feed it to rate(), avg_over_time(), etc.

http_requests_total[5m]
# → all samples in the last 5 minutes

Scalar

basic

A single floating-point number. Returned by literals and the scalar() function.

3.14
scalar(sum(up))

String

basic

Currently used only as function arguments. You can't query a string.

"five minutes"

Selectors & matchers

Picking the series you want

By metric name

basic

The bare name selects every series that shares it.

node_cpu_seconds_total

Label equals =

basic

Exact string match. Most common matcher in everyday queries.

http_requests_total{job="api", method="GET"}

Not equal !=

basic

Exclude a label value. Useful for filtering out a noisy instance or status.

http_requests_total{status!="200"}

Regex match =~

basic

RE2 regex, fully anchored automatically. Great for OR-ing several values.

http_requests_total{status=~"5.."}
up{job=~"api|web|worker"}

Negative regex !~

advanced

Series whose label does not match the pattern.

requests_total{path!~"/health.*"}

Match a missing label

advanced

An empty string matches series where the label is unset. Pair with = or !=.

# series WITHOUT an env label
up{env=""}

# series that DO have one
up{env!=""}

Match metric name as a label

advanced

The metric name lives in the special __name__ label, so you can regex-match it too.

{__name__=~"node_cpu_.*", cpu="0"}

Offset

advanced

Shifts the evaluation time backward. @ pins it to a Unix timestamp.

http_requests_total offset 5m

# pin to a specific moment
http_requests_total @ 1672531200

Time & duration syntax

Windows, ranges, and subqueries

Duration units

basic

Combine integer + unit. Units can be chained, biggest first, no spaces.

5m      # 5 minutes
1h30m   # 1.5 hours
7d      # 7 days
1y2w    # 1 year 2 weeks
# units: ms s m h d w y

Range vector window

basic

Square brackets after a selector capture all samples in that window.

node_network_receive_bytes_total[5m]

Subquery

advanced

[range:resolution] — evaluates an instant-vector expression over a range, like a recording rule on the fly. Resolution is optional.

max_over_time(
  rate(http_requests_total[5m])[1h:1m]
)

Cost: subqueries re-evaluate the inner expression at each step. Prefer recording rules for hot paths.

@ start() / @ end()

advanced

Pins evaluation to the start or end of the surrounding range query — handy for rendering "value at the moment of an alert".

http_requests_total @ start()

Aggregation operators

Roll up an instant vector across series

sum

basic

Add values across series. Almost always combined with by or without.

sum by (job) (http_requests_total)

avg

basic
avg by (instance) (node_load1)

min / max

basic
max by (job) (up)

count

basic

Number of series in each group. Use count_values for value buckets.

count by (job) (up == 1)

by vs without

basic

by keeps only listed labels; without drops them and keeps the rest. Pick whichever is shorter to type.

sum by (code) (requests)
sum without (instance, pod) (requests)

topk / bottomk

advanced

K highest/lowest series per group — keeps labels, unlike sum.

topk(5, rate(http_requests_total[5m]))

quantile

advanced

φ-quantile (0–1) across series. For histogram quantiles, see histogram_quantile.

quantile by (job) (0.95, go_goroutines)

stddev / stdvar

advanced

Population standard deviation / variance across series in each group.

stddev by (cluster) (node_load1)

group

advanced

Returns a constant 1 per group — useful as a left side in many-to-one joins.

group by (cluster, namespace) (kube_pod_info)

Functions

The workhorses, ordered roughly by how often you'll reach for them

rate(v range)

basic

Per-second average rate of a counter over the window. Resets are handled automatically. Use this for graphs and alerts on counters.

rate(http_requests_total[5m])

Window must contain at least 2 samples — make it ≥ 4× scrape interval.

irate(v range)

advanced

Rate based on the last two samples only — reactive but spiky. Good for short-lived dashboards, bad for alerts.

irate(http_requests_total[1m])

increase(v range)

basic

Total counter increase over the window. Equivalent to rate × seconds, but reads more naturally.

increase(errors_total[1h])

delta(v range)

basic

First-to-last difference for a gauge. Don't use on counters (use increase).

delta(cpu_temp_celsius[2h])

histogram_quantile(φ, b)

advanced

φ-quantile (0–1) from a classic histogram's _bucket series. Always pair with rate + a sum by (le, …).

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

predict_linear(v range, t)

advanced

Linear regression — predicts the value t seconds from now. Classic disk-full alert.

predict_linear(
  node_filesystem_avail_bytes[1h], 4 * 3600
) < 0

*_over_time(v range)

basic

Aggregate a range vector along the time axis (per series). Family includes avg / min / max / sum / count / quantile / stddev / last.

avg_over_time(cpu_usage[10m])
max_over_time(queue_depth[1h])
last_over_time(build_info[24h])

absent(v) / absent_over_time

advanced

Returns 1 when the input has no series — perfect for "did this metric stop reporting?" alerts.

absent(up{job="api"})
absent_over_time(up{job="api"}[5m])

changes / resets

advanced

changes counts value changes (gauges); resets counts counter resets in the window.

changes(leader_id[1h])
resets(process_start_time_seconds[1h])

label_replace

advanced

Rewrite a label using a regex on another label. Most common use: aligning labels for a join.

label_replace(
  up, "host", "$1", "instance", "(.*):.*"
)

label_join

advanced

Concatenate label values into a new label using a separator.

label_join(up, "id", "/", "job", "instance")

vector(s) / scalar(v)

advanced

Bridge between scalar and vector worlds — useful in arithmetic against constants.

vector(1)
scalar(sum(up))

time()

advanced

Unix timestamp (seconds) of the evaluation. Combine with process_start_time_seconds for uptime.

time() - process_start_time_seconds

clamp / clamp_min / clamp_max

advanced

Squeeze values into a range. Handy for sanitizing dashboards.

clamp(cpu_pct, 0, 100)

sort / sort_desc

basic

Sort the result vector by sample value. Affects table view, not graphs.

sort_desc(rate(errors_total[5m]))

Arithmetic & logic

basic

Vectors line up by matching label sets. Use on()/ignoring() + group_left/right for many-to-one joins.

requests{code="5xx"}
  / ignoring(code) group_left
requests

Performance & gotchas

Things that bite once you're past beginner

rate() goes inside sum()

Always: sum(rate(...)), never rate(sum(...)). Counters reset per series; aggregating first hides the resets and produces nonsense.

Range window ≥ 4× scrape interval

rate/increase need at least 2 samples. With a 30s scrape, [1m] is fragile. [2m] is the safe minimum, [5m] is conventional.

increase() can return non-integers

Prometheus extrapolates to the window edges. increase(x[1h]) may report 4.32 even though counters are integers. Don't be alarmed.

Match the function to the metric type

rate / irate / increase / resets → counters. delta / deriv / predict_linear → gauges. Mixing them silently produces wrong numbers.

Keep le when summing histograms

histogram_quantile needs the le label. sum by (le, …)(rate(..._bucket[5m])) — never drop it.

Watch label cardinality

User IDs, request paths, and trace IDs as labels will burn your TSDB. Aggregate them away early in queries; never expose them on hot metrics.

Subqueries are expensive

[1h:1m] means 60 inner evaluations per output point. If you reach for one in a dashboard panel, it probably wants to be a recording rule.

Binary ops need matching label sets

If a / b drops to empty, the two sides have different labels. Use on(...) to whitelist matching labels, ignoring(...) to drop the noisy ones, and group_left/group_right for many-to-one.