Prometheus compatibility

PromQL in Elasticsearch reads metrics stored in time series data streams (TSDS). Most PromQL queries run unchanged and return the same results as Prometheus 3.x. This page lists the PromQL features Elasticsearch doesn't support yet and where its behavior differs by design, so you can tell in advance whether a query runs and what it returns. It applies to both the Prometheus-compatible HTTP API and the ES|QL PROMQL source command, unless a section says otherwise.

The following sections follow the Prometheus querying documentation, so each difference sits under the heading that documents the construct in Prometheus. Each section has up to three parts:

  • Supported: what runs and returns the same result as in Prometheus.
  • Not supported yet: Elasticsearch rejects the query with an error. Prometheus supports every construct in these tables, some only with a feature flag.
  • Behaves differently: the query runs in both, but the result differs.

Rows link the issue that tracks the work, where there is one. A row with a version badge applies only to those versions. A row without one applies to every version.

Sections follow Querying basics in the Prometheus documentation.

In the examples on this page, h is a native histogram metric.

Supported

Native histograms, stored as exponential_histogram fields. histogram_quantile, histogram_count, histogram_sum, histogram_avg, increase, and sum support them, such as histogram_quantile(0.9, sum by (job) (increase(h[5m]))).

histogram_fraction and changes support them too.

Classic histograms, stored as _bucket series with an le label, such as histogram_quantile(0.9, sum by (le) (rate(x_bucket[5m]))).

Not supported yet

# Construct Example Workaround Issue
1 Native histogram as the result h, increase(h[5m]) Wrap it in a histogram function, such as histogram_count #152263

Supported

Range vectors as function arguments, such as rate(x[5m]). In the PROMQL command, the range can be left out, such as rate(x). Refer to Implicit range selectors.

Not supported yet

# Construct Example Workaround Issue
2 Range vector as the result of an instant query x[5m] - #164220

Supported

String literals in single quotes, double quotes, or backticks. Float literals, including scientific notation, hexadecimal, Inf, and NaN, such as 1e3 and 0x1f. Time durations, such as 1h30m, and duration arithmetic, such as x[5m + 1m].

Not supported yet

# Construct Example Workaround Issue
3 step(), range(), min_of(), and max_of() in a duration (Prometheus: promql-duration-expr feature flag) x[step()], x[min_of(5m, step())] Use a fixed duration #164221

Supported

A metric name with the label matchers =, !=, =~, and !~, such as http_requests_total{job="api", code=~"5.."}.

Not supported yet

# Construct Example Workaround Issue
4 Selector without a metric name {job="api"} - #146977
5 Regex matcher on __name__ {__name__=~"node_.*"} Combine the metrics with or: node_cpu or node_mem #146977

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
6 Negative matcher on __name__ {__name__!="up", job="node"} No results Every other matching metric #146977

Supported

A range selector with a duration, such as x[5m] or x[1h30m].

Not supported yet

# Construct Example Workaround Issue
7 anchored and smoothed modifiers (Prometheus: promql-extended-range-selectors feature flag) increase(x[5m] anchored) - #164222

Supported

offset on instant and range selectors, such as x offset 5m and rate(x[5m] offset 1h).

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
8 Negative offset at the end of the query range x offset -5m No value at the last steps. Samples after the query end aren't read Reads samples after the query end -

Not supported yet

# Construct Example Workaround Issue
9 @ modifier x @ 1700000000, x @ end() - #164139

Not supported yet

# Construct Example Workaround Issue
10 Subquery max_over_time(rate(x[5m])[1h:1m]) - #164139

Supported

A selector without a range, such as http_requests_total, returns the latest sample of each series within the lookback window at each evaluation time.

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
11 Lookback in instant queries /api/v1/query Five minutes, fixed Five minutes by default, configurable with lookback_delta #149939
12 Lookback in range queries /api/v1/query_range One step. A series disappears at the first step without its own sample Five minutes. The last sample repeats until then -
13 Staleness markers - Ignored. A series disappears once its samples leave the lookback window A series disappears at its staleness marker -

Sections follow Operators in the Prometheus documentation. Each operator reference entry lists further differences.

Supported

+, -, *, /, %, and ^ between scalars, between an instant vector and a scalar, such as rate(x[5m]) * 60, and between two instant vectors. Vector matching lists which pairs of instant vectors match.

Not supported yet

# Construct Example Workaround Issue
14 Native histogram operands h * 2, h1 + h2 - #152263

Not supported yet

# Construct Example Workaround Issue
15 atan2 x atan2 y - #164153

Supported

==, !=, >, <, >=, and <= between an instant vector and a number, such as rate(x[5m]) > 100, with or without bool, such as x > bool 100.

Not supported yet

# Construct Example Workaround Issue
16 Filter with a scalar as the first operand 100 < x, time() <= x Swap the operands: x > 100 #158184
17 Non-literal scalar as the second operand x > time(), x > scalar(y) - #158184
18 Two vectors, or bool with a scalar as the first operand x > y, 100 > bool x For bool, swap the operands: x < bool 100 #158184
19 Comparison inside another expression (x > 100) + 1, count(x > 100) - #158184

Supported

or between instant vectors, such as rate(a[5m]) or rate(b[5m]). An or chain takes at most 8 operands by default.

Change the limit with the esql.query.max_branch_count_per_merge cluster setting.

Not supported yet

# Construct Example Workaround Issue
20 and, unless a and b, a unless b - #158184
21 or with on(...) or ignoring(...) a or on(job) b - #158184
22 or inside another expression (sum(a) or sum(b)) * 2, count(a or b) - #158184

Supported

Arithmetic between unaggregated metrics stored together in the same time series, such as network.bytes_in / network.connections, without explicit vector matching modifiers.

Arithmetic between operands aggregated without grouping, such as sum(a) / sum(b).

One-to-one, many-to-one, and one-to-many matching between operands aggregated with by(...), including on, ignoring, group_left, and group_right. For example: sum by (instance) (a) / on(instance) sum by (instance) (b).

Not supported yet

# Construct Example Workaround Issue
23 Different unaggregated metrics stored as separate series with a __name__ dimension, such as data ingested with Prometheus remote write a / b Aggregate both by the labels to match on: sum by (instance) (a) / sum by (instance) (b) #158184
24 on, ignoring, group_left, or group_right with an operand that isn't aggregated a / on(instance) b, a * on(pod) group_left(node) b Aggregate both operands by the labels to match on #157669
25 Operands aggregated differently sum(a) / b, b / scalar(sum(a)), topk(2, a) * b Aggregate both operands alike: sum(a) / sum(b). A number, such as sum(a) * 2, and or are supported #158184
26 Nested aggregation next to an operand that reads a metric count(count by (pod) (a)) / sum(b) - #158184
27 A different offset per operand rate(x[5m]) / rate(x[5m] offset 1h) or is supported #158184
28 label_replace or label_join as an operand label_replace(a, ...) * on(pod) group_left(node) b - #157672
29 fill(), fill_left(), and fill_right() (Prometheus: promql-binop-fill-modifiers feature flag) a + fill(0) b - #164223

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
30 Different unaggregated metrics stored as separate series a / b No results Series matched on their other labels #158184

Supported

  • sum, avg, min, max, count, quantile, stddev, and stdvar, with by(...), without(...), or no grouping.
  • topk, bottomk, limitk, and limit_ratio.

Not supported yet

# Construct Example Workaround Issue
31 group, count_values group by (job) (x), count_values("v", x) - #164009, #136248
32 topk, bottomk, limitk, or limit_ratio with without(...) topk without (instance) (1, x) Use by(...) #157671
33 Parameter that isn't a number literal topk(scalar(a), b), quantile(scalar(a), b) Use a number #164225
34 Nested without(...) sum without (pod) (max without (container) (x)) Use by(...) #157671
35 without(...) as an operand sum without (method) (x) * 2 Use by(...) #145308
36 Grouping by a label named step sum by (step) (x) step is an output column of the PROMQL command -

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
37 quantile quantile(0.9, x) Approximate, using t-digest Exact, with linear interpolation -
38 limitk, limit_ratio limitk(10, x) A different, equally valid subset A deterministic pseudo-random subset, with the promql-experimental-functions feature flag -

Rows follow Functions in the Prometheus documentation and are sorted by function name. Each function reference entry lists further differences.

Supported

The functions in the PromQL functions reference, such as rate, increase, histogram_quantile, label_replace, and the <aggregation>_over_time functions.

Not supported yet

# Construct Example Workaround Issue
39 Functions listed under Not supported yet absent(x), resets(x[1h]), info(x) - #164001, #164003, #164002
40 Parameter that isn't a number literal clamp_min(a, scalar(b)), histogram_quantile(scalar(a), b) Use a number #164225
41 histogram_quantile or histogram_fraction over topk, bottomk, limitk, or limit_ratio histogram_quantile(0.9, topk(5, a)) Aggregate with by(...) first: histogram_quantile(0.9, topk(5, sum by (job, le) (a))) #164226
42 histogram_quantile or histogram_fraction over without(...) histogram_quantile(0.9, sum without (instance) (rate(x[5m]))) Use by(...) #157671
43 histogram_stddev, histogram_stdvar histogram_stddev(h) - #164152
44 irate, delta on native histograms irate(h[5m]) - #152263
45 label_replace or label_join outside by(...) label_replace(x, "dst", "$1", "instance", "(.*):.*") Use it inside by(...): sum by (dst) (label_replace(...)) #157672
46 label_replace or label_join over an aggregation label_replace(sum by (job) (x), ...) - #157672
47 label_replace or label_join over anything other than a by(...) aggregation, or that overwrites a grouping label label_replace(sum without (instance) (x), "dst", "$1", "job", "(.*)"), label_replace(sum by (job) (x), "job", "$1", "job", "(.*)") Aggregate with by(...) and write a new label: label_replace(sum by (job) (x), "dst", "$1", "job", "(.*)") #157672
48 label_replace or label_join that writes a label named step label_replace(x, "step", ...) step is an output column of the PROMQL command -
49 predict_linear with a horizon computed by a function predict_linear(x[1h], time()), predict_linear(x[1h], scalar(y)) Use a number, a duration literal, or arithmetic on literals #164225
50 rate on native histograms rate(h[5m]) Use increase: histogram_quantile(0.99, sum by (job) (increase(h[5m]))) #152263
51 sort or sort_desc below the top level sum(sort(x)) Remove it. Sorting has no effect there #164227
52 step(), range(), start(), end() (Prometheus: promql-experimental-functions feature flag) time() - start() - #164228

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
53 absent_over_time with no data or over a gap absent_over_time(x{job="none"}[5m]) No results 1, with the labels of the selector's equality matchers -
54 histogram_count, histogram_sum, or histogram_avg on classic buckets histogram_count(rate(x_bucket[5m])) An error. They accept only native histograms No results -
55 quantile_over_time quantile_over_time(0.9, x[5m]) Approximate, using t-digest Exact, with linear interpolation -
56 rate, increase rate(x[5m]) Computed over fixed time buckets, interpolated at bucket boundaries. Results can differ slightly Extrapolated within each range window -

Sections follow HTTP API in the Prometheus documentation. They apply only to the Prometheus-compatible HTTP API.

Supported

GET requests, and responses in the Prometheus JSON format.

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
57 Form-encoded POST Content-Type: application/x-www-form-urlencoded 406 Not Acceptable.
Accepted when security is enabled, xpack.security.http.ssl.enabled is true, and the request is authenticated. TLS must terminate at Elasticsearch. Otherwise, use GET
Accepted -
58 warnings and infos quantile(2, x) Not returned. The only warning is results truncated due to limit Returned -

Supported

Instant queries and range queries.

Not supported yet

# Construct Example Workaround Issue
59 Query parameters that the PromQL HTTP API doesn't list lookback_delta=1m, stats=all Configure clients to omit them. Elasticsearch returns 400 Bad Request #149939

Supported

The type and unit of each metric in /api/v1/metadata.

Behaves differently

# Construct Example Elasticsearch Prometheus Issue
60 help in /api/v1/metadata GET /api/v1/metadata Empty The metric's HELP text #157230

Not supported yet

# Construct Example Workaround Issue
61 Exemplar queries GET /api/v1/query_exemplars In Grafana, remove the links under Data sources → Elasticsearch → Exemplars #154786