by(), without()

The max() aggregation (as well other aggregations) on its own discards all the labels and the metric name, and provides the aggregated result with no labels, since the max is selected across all the time series. It is no longer the sys_cpu_host_seconds_total{mode=sys} or sys_cpu_host_seconds_total{mode=user}, but the max value of them.

To control the labels in an aggregation you have 2 clauses to use:

  1. without() - removes the listed labels from the result vector, while all other labels are preserved in the output

F.e. if he metric has 2 labels mode and job and you want to preserve the job label while take the max among mode-s you could use:

max(sys_cpu_host_seconds_total)without(mode)

The result vector will return the max value preserving all the rest labels but the mode:

{job='demo-1'} 3065880.72
{job='demo-2'} 1760763.05
  1. by() - does the opposite and drops labels that are not listed in the by clause, even if their label values are identical between all elements of the vector

F.e. if he metric has 2 labels mode and job and you want to preserve the job label while take the max among job-s you could use:

max(sys_cpu_host_seconds_total)by(job)

The result vector will be the same, but it contains only the job label:

{job='demo-1'} 3065880.72
{job='demo-2'} 1760763.05

"__name__" label

The trick with the metric name is that internally it is a label with the special name of "__name__". So you could use it to preserve the metric name if you really need it:

max(sys_cpu_host_seconds_total)by(__name__, job)

The result vector will "preserve" the metric name, however it's just a trick since the result is no longer the metric you aggregate on, but the aggregation result:

sys_cpu_host_seconds_total{job='demo-1'} 3065880.72
sys_cpu_host_seconds_total{job='demo-2'} 1760763.05

topk(1, ...)

topk and bottomk are different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector, so you coul use this trick to preserve all the labels along with the name:

topk(1, sys_cpu_host_seconds_total)

The result vector will "preserve" all the labels along with the metric name:

sys_cpu_host_seconds_total{mode='sys', job='demo-1'} 3065880.72
Answer from star67 on Stack Overflow
🌐
Prometheus
prometheus.io › docs › prometheus › latest › querying › functions
Query functions | Prometheus
This function has to be enabled via the feature flag --enable-feature=promql-experimental-functions. max_of(a scalar, b scalar) returns the larger of the two scalar values a and b.
🌐
Promlabs
promlabs.com › promql-cheat-sheet
PromLabs | PromQL Cheat Sheet
max_over_time( rate( demo_api_request_duration_seconds_count[5m] )[1h:] ) Open in PromLens · For more details about PromQL, see the official PromQL documentation: Basics · Operators · Functions · Examples · Want to learn more?
Top answer
1 of 2
3

by(), without()

The max() aggregation (as well other aggregations) on its own discards all the labels and the metric name, and provides the aggregated result with no labels, since the max is selected across all the time series. It is no longer the sys_cpu_host_seconds_total{mode=sys} or sys_cpu_host_seconds_total{mode=user}, but the max value of them.

To control the labels in an aggregation you have 2 clauses to use:

  1. without() - removes the listed labels from the result vector, while all other labels are preserved in the output

F.e. if he metric has 2 labels mode and job and you want to preserve the job label while take the max among mode-s you could use:

max(sys_cpu_host_seconds_total)without(mode)

The result vector will return the max value preserving all the rest labels but the mode:

{job='demo-1'} 3065880.72
{job='demo-2'} 1760763.05
  1. by() - does the opposite and drops labels that are not listed in the by clause, even if their label values are identical between all elements of the vector

F.e. if he metric has 2 labels mode and job and you want to preserve the job label while take the max among job-s you could use:

max(sys_cpu_host_seconds_total)by(job)

The result vector will be the same, but it contains only the job label:

{job='demo-1'} 3065880.72
{job='demo-2'} 1760763.05

"__name__" label

The trick with the metric name is that internally it is a label with the special name of "__name__". So you could use it to preserve the metric name if you really need it:

max(sys_cpu_host_seconds_total)by(__name__, job)

The result vector will "preserve" the metric name, however it's just a trick since the result is no longer the metric you aggregate on, but the aggregation result:

sys_cpu_host_seconds_total{job='demo-1'} 3065880.72
sys_cpu_host_seconds_total{job='demo-2'} 1760763.05

topk(1, ...)

topk and bottomk are different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector, so you coul use this trick to preserve all the labels along with the name:

topk(1, sys_cpu_host_seconds_total)

The result vector will "preserve" all the labels along with the metric name:

sys_cpu_host_seconds_total{mode='sys', job='demo-1'} 3065880.72
2 of 2
3

You should use topk for selecting the metric with the maximum value:

topk(1, 
  sys_cpu_host_seconds_total{mode="sys"}
  or sys_cpu_host_seconds_total{mode="user"}
)

Note that topk(k, q) query can return more than k time series when this query is used for building a graph in Grafana. This is because topk(k, q) independently selects top k series per each timestamp displayed on the graph. If you want up to k series with max malues to be displayed on the graph, then take a look at topk_max, topk_avg and other topk_* functions in MetricsQL - this is PromQL-compatible query language in Prometheus-like system I work on.

People also ask

What is PromQL?
PromQL (Prometheus Query Language) is the query language built into Prometheus for selecting, filtering, and aggregating time series data. You use it to write expressions that power dashboards, alerts, and ad-hoc metric analysis.
🌐
last9.io
last9.io › blog › promql-cheat-sheet
PromQL Cheat Sheet: Queries, Functions, and Labels | Last9
How do I predict future resource usage in PromQL?
Use predict_linear(metric[window], seconds). For example, predict_linear(node_filesystem_free_bytes[30d], 86400 7) predicts disk space 7 days from now based on the last 30-day trend. Use a long lookback window for more stable predictions.
🌐
last9.io
last9.io › blog › promql-cheat-sheet
PromQL Cheat Sheet: Queries, Functions, and Labels | Last9
How do I filter by label values in PromQL?
Use curly brace selectors: http_requests_total{job="api", status="500"} for exact matches, {status=~"5.."} for regex matches, and {status!="200"} to exclude a value. Multiple label filters combine with AND logic.
🌐
last9.io
last9.io › blog › promql-cheat-sheet
PromQL Cheat Sheet: Queries, Functions, and Labels | Last9
🌐
Iximiuz
iximiuz.com › en › posts › prometheus-functions-agg-over-time
Prometheus Cheat Sheet - Moving Average, Max, Min, etc (Aggregation Over Time)
July 2, 2021 - What only matters for PromQL is an expression type. I.e., it wouldn't allow you to call a function that expects an instant vector with a range vector argument. However, a range vector of gauges is physically indistinguishable from a range vector of counters. And here we go... functions min_over_time(), max_over_time(), avg_over_time(), sum_over_time(), stddev_over_time(), and stdvar_over_time() makes sense to use only with gauge metrics.
🌐
Google Groups
groups.google.com › g › prometheus-users › c › Evum5I8ARDs
Calculate Max over time on Sum function
July 13, 2017 - The expression sum(max(...)) is not equivalent to max(sum(...)). As Jeremy has described, that requires recording the sum in a new time series with recording rules. PromQL currently only supports constructing a range vector from a time series, but not from the result of an expression.
🌐
Prometheus
discuss.prometheus.io › promql
Finding timestamp of min (or max) reading - PromQL - Prometheus Monitoring System
November 17, 2022 - I’m trying to find the timestamp for the maximum or minimum reading. From what I understand this is difficult but a workaround was posted in issue #8966. So let’s say I have a metric called “temp” which is temperature in celsius. min_over_time(temp[12h]) looks like this: which is fine; the minimum temperature in the last 12 hours was about 20.8°C. So in following the posted workaround I would want to filter for all values that equal that 20.8: scalar(min_over_time(temp[12h])) == temp Th...
🌐
GitHub
github.com › prometheus › prometheus › issues › 12172
promql (histograms): Implement max/min for native histograms. · Issue #12172 · prometheus/prometheus
March 22, 2023 - Proposal A max (and min) of a native histograms could be defined as the upper (lower) bound of the highest (lowest) bucket, thereby yielding a strict upper (lower) limit of the observed values. (The pull-based stateless scrape model of P...
Author: prometheus
Find elsewhere
🌐
Last9
last9.io › blog › promql-cheat-sheet
PromQL Cheat Sheet: Queries, Functions, and Labels | Last9
September 12, 2024 - This finds the spread between max and min CPU usage over the last hour, calculated every 5 minutes. Useful for detecting CPU spikes within a time window. PromQL offers several specialized aggregation functions to help you analyze and summarize ...
🌐
Last9
last9.io › blog › guide-to-prometheus-query-language
PromQL: A Developer's Guide to Prometheus Query Language | Last9
February 26, 2026 - As you become more comfortable with basic PromQL, you’ll want to leverage its more advanced features to gain deeper insights into your Prometheus metrics. Subqueries allow you to perform query operations over a range of evaluation times. They’re particularly useful for calculating “moving” averages or detecting slow-moving trends. Here’s an example using a time duration: max_over_time(rate(http_requests_total[5m])[1h:5m])
🌐
Google Groups
groups.google.com › g › prometheus-users › c › gAMhzzp0Cb0
max_over_time not working as expected - want to get the 3 most recent values higher than a specific threshold
August 28, 2024 - If you want the maximum values of each timeseries over the last 24 hours, then you want max_over_time(foo[24h]) - try it in the PromQL web interface.
🌐
GitHub
github.com › prometheus › prometheus › issues › 5177
min and max as binary operators · Issue #5177 · prometheus/prometheus
February 1, 2019 - Proposal Adding min and max as binary operators to promql. Use case. Why is this important? I've got two different timeseries and want to join them by using the maximum value of the two. For example timeseries_a{foo="bar"} 4 timeseries_b...
Author: prometheus
🌐
Grafana
community.grafana.com › prometheus
How to make Grafana/Prometheus summarize values by max or peak when zooming out? - Prometheus - Grafana Labs Community Forums
August 27, 2021 - My issue is that whenever the chart resolution goes down a step, as I increase the time interval, I lose all the peaks. Here is the chart spanning 20 hours: Here is the same chart spanning ...
🌐
DEV Community
dev.to › sre_panchanan › decoding-promql-a-deep-dive-into-prometheus-query-language-4h23
Decoding PromQL: A Deep Dive into Prometheus Query Language - DEV Community
November 12, 2024 - Here are some types of aggregation ... min(): Identifies the minimum value across time series. max(): Identifies the maximum value across time series....
🌐
VictoriaMetrics
docs.victoriametrics.com › metricsql
VictoriaMetrics: MetricsQL
If the lookbehind window is skipped in square brackets, then it is automatically calculated as max(step, scrape_interval), where step is the query arg value passed to /api/v1/query_range or /api/v1/query , while scrape_interval is the interval between raw samples for the selected time series. This allows avoiding unexpected gaps on the graph when step is smaller than the scrape_interval. Metric names are stripped from the resulting rollups. Add keep_metric_names modifier in order to keep metric names. This function is supported by PromQL.
Top answer
1 of 2
14

It is possible.

Example query:

max_over_time(
   irate( messages_in_total[2m] )[1d:1m]
)

This will:

  1. take last 1 day
  2. For every 1 minute in that 1 day range it will execute irate( messages_in_total[2m] )
  3. Combine that into range vector
  4. Call max_over_time on all results

See subquery documentation for more information!

2 of 2
-1

While the answer returns the maximum per-second rate over the last 24 hours for messages_in_total metric, it has the following potential issues:

  • It may skip a part of raw samples if the interval between them (aka scrape_interval) is smaller than one minute. This can be fixed by reducing the step value in square brackets after the colon, so it doesn't exceed the scrape_interval.
  • It may return an empty result or incomplete result if the scrape interval exceeds 2m (e.g. 2 minutes). This can be fixed by increasing the lookbehind window in the inner square brackets from 2m to the value exceeding 2x scrape_interval.
  • It may become very slow and resource hungry because of subquery overhead.
  • Subqueries are easy to mis-use, so they would silently return unexpected results.

While Prometheus doesn't provide the reliable and easy to use solution for these issues, other Prometheus-like systems may have the solution. For example, the following MetricsQL query returns the maximum, the minimum and the average per-second increase rates for messages_in_total time series for the last 24 hours:

rollup_rate(messages_in_total[1d])

It uses rollup_rate function. If you need only the maximum per-second rate, then the query can be wrapped into label_match function, which leaves only time series with rollup="max" label:

label_match(
  rollup_rate(messages_in_total[1d]),
  "rollup", "max"
)
🌐
SigNoz
signoz.io › guides › essential promql cheat sheet - master prometheus queries
Essential PromQL Cheat Sheet - Master Prometheus Queries | SigNoz
November 29, 2024 - Use PromQL functions like max_over_time, quantile_over_time, or stddev_over_time to detect anomalies:
🌐
GitHub
github.com › jitendra-1217 › promql.cheat.sheet › blob › master › readme.md
promql.cheat.sheet/readme.md at master · jitendra-1217/promql.cheat.sheet
→ min, max The min and max aggregators return the minimum or maximum value within a group as the value of the group.
Author: jitendra-1217