﻿---
title: Use Elasticsearch as a Prometheus data source in Grafana
description: Use Elasticsearch as a Prometheus data source in Grafana to query metrics with PromQL, power autocomplete and Metrics Drilldown, and reuse existing dashboards.
url: https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-grafana
products:
  - Elasticsearch
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Generally available since 9.5, Preview in 9.4
---

# Use Elasticsearch as a Prometheus data source in Grafana
Elasticsearch exposes a [Prometheus-compatible HTTP API](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api) under the `/_prometheus/` prefix.
Because the API follows the [Prometheus query API](https://prometheus.io/docs/prometheus/latest/querying/api/), you can point Grafana's built-in **Prometheus** data source directly at Elasticsearch without sidecars, adapters, or pipeline changes.
Grafana then treats Elasticsearch like any other Prometheus backend: dashboard panels, query autocompletion, template variables, and [Metrics Drilldown](https://grafana.com/docs/grafana/latest/visualizations/simplified-exploration/metrics/) all use PromQL against metrics stored in Elasticsearch [time series data streams](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/time-series-data-stream-tsds) (TSDS).
This guide covers connecting Grafana to Elasticsearch. To ingest metrics from Prometheus, refer to [Prometheus remote write](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write).
<important>
  Before you build dashboards, review [Prometheus compatibility](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility).
  Behavior differs from upstream Prometheus in a few areas, and some PromQL constructs are not evaluated yet.
  Knowing these up front helps you tell an expected limitation apart from a real problem when [troubleshooting](#promql-grafana-troubleshooting).
</important>


## Before you begin

You need:
- The Elasticsearch endpoint for your deployment.
  On Elasticsearch Serverless, find it in the Elastic Cloud console under **Manage > Application endpoints, cluster and component IDs**, next to **Elasticsearch**.
  The endpoint looks like `https://<project-id>.es.<region>.<provider>.elastic.cloud`.
- An [API key](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/deploy-manage/api-keys/elasticsearch-api-keys) for Grafana to query metrics.

Create an API key scoped to read metrics data streams only, so a leaked Grafana key cannot be used to write data.
In **Control security privileges**, use a role descriptor such as:
```json
{
  "query": {
    "indices": [
      {
        "names": ["metrics-*.prometheus-*", "exemplars-*.prometheus-*"],
        "privileges": ["read", "view_index_metadata"],
        "query": {
          "terms": { "_tier": ["data_hot", "data_warm"] }
        }
      }
    ]
  }
}
```

The `metrics-*.prometheus-*` and `exemplars-*.prometheus-*` patterns scope the key to metrics ingested through [Prometheus remote write](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write), which is what this guide focuses on.
Because the key can read only those data streams, Grafana queries stay limited to that Prometheus data even when they use the default `/_prometheus/` endpoint.
When the key is created, copy the **Encoded** value (select the **Encoded** format if Kibana offers a format dropdown). This is the value you pass directly as `ApiKey <encoded>`; you cannot retrieve it again after closing the dialog.
<tip>
  You can configure a separate Grafana data source per index pattern to give teams scoped access to their own metrics.
  Add an index expression after `/_prometheus/`, for example `https://<es_endpoint>/_prometheus/metrics-prod-*`.
  Index aliases are also supported as the index expression – you need to scope the API key to the alias name rather than the underlying index pattern.
  Refer to [Index scoping](/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api#promql-http-api-index-scope).
</tip>


## Add the data source

Configure the data source through the Grafana UI or through [provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources).

### Add the data source in the UI

1. In Grafana, go to **Connections → Data sources → Add data source**, then choose **Prometheus**.
2. Set the following:
   - **Name**: `Elasticsearch`
- **Prometheus server URL**: `https://<es_endpoint>/_prometheus`
- Under **Custom HTTP Headers**, add a header:
  - **Header**: `Authorization`
- **Value**: `ApiKey <query_api_key>`
3. Click **Save & test**.

Grafana confirms the connection, and autocompletion starts working in the query editor.
The data source has more options than the ones listed here. For the authoritative steps and the full option reference, refer to the [Grafana Prometheus data source documentation](https://grafana.com/docs/grafana/latest/datasources/prometheus/configure/).
<note>
  Grafana sends Prometheus queries with `POST` by default, which Elasticsearch accepts on authenticated HTTPS endpoints such as Elasticsearch Serverless.
  If TLS terminates before Elasticsearch and the node receives plain HTTP, set the data source **HTTP method** to `GET` instead.
  Refer to [Form-encoded POST requests](/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility#prometheus-compatibility-format-overview).
</note>


### Add the data source with provisioning

To provision the data source from a file, create `provisioning/datasources/datasource.yml`, replacing `<es_endpoint>` and `<query_api_key>`:
```yaml
apiVersion: 1

datasources:
  - name: Elasticsearch
    type: prometheus
    access: proxy
    url: "https://<es_endpoint>/_prometheus"
    uid: elasticsearch-prometheus
    isDefault: true
    jsonData:
      httpHeaderName1: Authorization
      # If TLS terminates before Elasticsearch (plain HTTP to the node), uncomment:
      # httpMethod: GET
    secureJsonData:
      httpHeaderValue1: "ApiKey <query_api_key>"
```

<note>
  `isDefault: true` overrides the existing default data source for all users in the Grafana instance.
  Set it to `false` if this deployment already has a default data source.
</note>

Mount the `provisioning` directory into Grafana at `/etc/grafana/provisioning`.

## Use dashboards

Existing Prometheus dashboards work against Elasticsearch without changes, as long as the PromQL they use is supported (refer to [Prometheus compatibility](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility)).
You can point an existing dashboard at Elasticsearch or build a new one on top of the data source.

### Point an existing dashboard at Elasticsearch

If a dashboard already runs against another Prometheus-compatible backend, switch its data source to Elasticsearch using the dashboard's [data source variable](https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/) dropdown (for dashboards that use one) or by editing the panels or the dashboard JSON model (for panels that reference a data source directly).

### Build a new dashboard

1. Go to **Dashboards → New → New dashboard → Add visualization**.
2. Select the **Elasticsearch** data source.
3. In the query editor, write a PromQL query (for example, `sum(up)`). Autocompletion and the metric browser are backed by the Elasticsearch [metadata and discovery endpoints](/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api#promql-http-api-metadata).
4. Choose a visualization, then save the dashboard.


## Troubleshooting

When a panel shows an error, returns no data, or renders something unexpected, the cause is in one of two layers:
- The Elasticsearch Prometheus API (owned by Elastic).
- The Grafana **Prometheus** data source, which translates Grafana's requests into Prometheus API calls (owned by Grafana, not Elastic).


### Step 1: Read the panel error

Grafana shows the data source's error message directly on the panel: hover or click the warning indicator in the panel's top-left corner.
For most errors Elasticsearch returns a descriptive message, and you can act on it without reproducing anything:
- A message that a PromQL construct or query parameter is **not supported yet** points to a [documented limitation](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility). Adjust the query, or wait for the feature. No issue needed.
- An authentication or authorization message (`401`/`403`) points to the API key. Refer to [Common symptoms](#promql-grafana-troubleshooting-symptoms).
- A `406 Not Acceptable` on every query points to the form-encoded `POST` requirement. Refer to [Common symptoms](#promql-grafana-troubleshooting-symptoms).

If the message is clear and matches a documented limitation, you're done.
If it is unclear, the panel returns wrong or empty data with no error, or you suspect the data source layer, continue to Step 2.

### Step 2: Inspect the request and response

If the panel error is unclear, or the panel returns wrong or empty data with no error, open the query inspector (panel menu → **Inspect → Query**) and run the query.
The inspector shows both the request Grafana sent (endpoint, `query`, `start`, `end`, `step`) and the raw response from Elasticsearch, which usually points straight at the cause.
To confirm Elasticsearch's behavior independently of Grafana, replay the same request directly against the [HTTP API](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api).
Replace `<es_endpoint>` and `<query_api_key>` with your values:
```bash
curl "https://<es_endpoint>/_prometheus/api/v1/query_range" \
  -H "Authorization: ApiKey <query_api_key>" \
  --data-urlencode 'query=up' \
  --data-urlencode 'start=2026-06-18T09:00:00Z' \
  --data-urlencode 'end=2026-06-18T10:00:00Z' \
  --data-urlencode 'step=15s'
```

For metric discovery, autocompletion, or template-variable problems, check the relevant [metadata endpoint](/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api#promql-http-api-metadata) instead (for example `/_prometheus/api/v1/labels` or `/_prometheus/api/v1/series`).

### Step 3: Decide where the issue belongs

- **Elasticsearch returns the expected data (in the inspector response or via curl), but the panel does not show it.**
  The cause is in Grafana, not Elasticsearch, and is almost always configuration or usage rather than a bug: panel and visualization settings, template variables, the dashboard time range, or transformations.
  Review the panel and dashboard configuration. For help, use the [Grafana documentation](https://grafana.com/docs/grafana/latest/) and [community forums](https://community.grafana.com/). Only [open a Grafana issue](https://github.com/grafana/grafana/issues) if you can confirm an actual bug in the Prometheus data source.
- **Elasticsearch returns an error (`"status": "error"`) or wrong data.**
  Before assuming an Elasticsearch problem, rule out a genuinely invalid query, which would fail against upstream Prometheus too.
  Confirm the expression is valid PromQL, for example with [PromLens](https://demo.promlens.com/).
  Then check whether the behavior is an expected difference:
  - The expression might use a [construct that Elasticsearch does not evaluate yet](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility). These return a `4xx` with `errorType: bad_data`.
- The request might include a [query parameter Elasticsearch does not support yet](/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility#prometheus-compatibility-expression-queries), which also fails with `4xx`.
- [Instant queries and staleness handling](/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility#prometheus-compatibility-staleness) differ from upstream Prometheus.
  If the query is valid and the behavior is not explained by a documented limitation, [open an Elasticsearch issue](https://github.com/elastic/elasticsearch/issues) with the request and the full response body.


### Common symptoms


| Symptom                                      | Likely cause                                           | What to check                                                                                                                                                                                                                                                      |
|----------------------------------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `406 Not Acceptable` on every query          | Form-encoded `POST` is not accepted by this deployment | Upgrade to the latest deployment version, and make sure TLS terminates at Elasticsearch. Refer to [Form-encoded POST requests](/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility#prometheus-compatibility-format-overview) |
| `401`/`403` errors                           | API key missing, invalid, or lacking privileges        | Verify the `Authorization: ApiKey <key>` header and that the key grants `read` and `view_index_metadata` on `metrics-*.prometheus-*` and `exemplars-*.prometheus-*`                                                                                                |
| Empty results, no error                      | No matching data in the time range or index scope      | Confirm metrics exist in the queried [index scope](/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api#promql-http-api-index-scope) and time window by reproducing the request directly                                               |
| `bad_data` error on a specific expression    | Unsupported PromQL construct                           | Compare the expression against [Prometheus compatibility](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility)                                                                                         |
| Recurring errors related to exemplar queries | `/api/v1/query_exemplars` is not implemented           | In Grafana, go to **Data sources → Elasticsearch → Exemplars** and disable all configured exemplar links                                                                                                                                                           |


## Further reading

- [HTTP API](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/promql-http-api): the Prometheus-compatible endpoints Grafana calls.
- [Prometheus compatibility](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/prometheus-compatibility): unsupported PromQL constructs and HTTP behavior.
- [Prometheus remote write](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write): ingest metrics into Elasticsearch.