A Practical PromQL Guide: Selectors, Rates, and Aggregations
August 10, 2026 · DevTools
Every PromQL query is three ideas stacked together: a metric selector, an optional range function, and an optional aggregation. Once you see that shape, the syntax stops being the hard part.
A selector is the metric name plus label matchers — http_requests_total{job="api", status=~"5.."}. The four operators are = (equals), != (not equals), =~ (regex matches), and !~ (regex does not match), and PromQL is forgiving of an empty matcher set: http_requests_total matches all series.
Counter metrics need a range function before they mean anything on a chart. rate(http_requests_total[5m]) converts a counter into a per-second average over the window, smoothing out resets; increase(...[5m]) reports the total growth over that window instead. Gauge-derived calculations skip this step.
Aggregations collapse a set of time series into one. sum by (status) (rate(http_requests_total[5m])) buckets the request rate per status code, while topk(5, ...) keeps only the five busiest series. Remember the order: selector first, range function next, aggregation outermost.
Two mistakes break most hand-written queries: applying rate to an instant vector (the window is what makes it a range vector), and aggregating without a by clause to keep your labels. When you are assembling one yourself, the PromQL Query Builder composes the same steps from a form — try it alongside Prometheus's own querying documentation to confirm what each part produces.