A Prometheus exporter for self-hosted Qdrant that surfaces per-collection, per-shard, cluster health, and search performance metrics — data that Qdrant's native /metrics endpoint does not expose.
Ships with a pre-built Grafana dashboard. One docker compose up command gives you the full observability stack.
Qdrant's native /metrics endpoint covers Go runtime stats and basic request counts. The rich per-collection data lives in the /telemetry and /cluster JSON endpoints but is never formatted as Prometheus metrics for self-hosted deployments. The detailed exporter (qdrant-cluster-exporter) and /sys_metrics endpoint are cloud-only.
This exporter closes that gap for everyone running Qdrant on bare metal, Docker, or any non-cloud environment.
| What you want to know | Native /metrics |
This exporter |
|---|---|---|
| Vectors per collection | ❌ | ✅ |
| Shard status per collection | ❌ | ✅ |
| Optimizer state (live) | ❌ | ✅ |
| Search latency by type | ❌ | ✅ |
| Indexing completeness ratio | ❌ | ✅ |
| RAM / disk per collection | ❌ | ✅ |
| Shard transfer progress | ❌ | ✅ |
| Raft consensus health | ❌ | ✅ |
The exporter is published to two registries — pick whichever is closer to your infrastructure:
| Registry | Image |
|---|---|
| Docker Hub | baselhusam/qdrant-exporter |
| GHCR | ghcr.io/baselhusam/qdrant-exporter |
Both are built and pushed simultaneously on every release. All examples below use the Docker Hub image name — substitute ghcr.io/baselhusam/qdrant-exporter to pull from GHCR instead.
Point it at your Qdrant and expose the metrics port. Prometheus scrapes :9153/metrics.
# Docker Hub
docker run -d \
--name qdrant-exporter \
-p 9153:9153 \
-e QDRANT_EXPORTER_QDRANT__NODES='[{"url":"http://host.docker.internal:6333"}]' \
--add-host host.docker.internal:host-gateway \
baselhusam/qdrant-exporter
# GHCR
docker run -d \
--name qdrant-exporter \
-p 9153:9153 \
-e QDRANT_EXPORTER_QDRANT__NODES='[{"url":"http://host.docker.internal:6333"}]' \
--add-host host.docker.internal:host-gateway \
ghcr.io/baselhusam/qdrant-exporterReplace
host.docker.internal:6333with your Qdrant address if it is not on the same host. If Qdrant requires an API key, add"api_key":"your-key"inside the JSON object.
# docker-compose.yml
services:
qdrant-exporter:
image: baselhusam/qdrant-exporter
container_name: qdrant-exporter
restart: unless-stopped
ports:
- "9153:9153"
environment:
QDRANT_EXPORTER_QDRANT__NODES: '[{"url":"http://host.docker.internal:6333"}]'
extra_hosts:
- "host.docker.internal:host-gateway"
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml:ro
extra_hosts:
- "host.docker.internal:host-gateway"docker compose up -d# docker-compose.yml
services:
qdrant-exporter:
image: baselhusam/qdrant-exporter
container_name: qdrant-exporter
restart: unless-stopped
ports:
- "9153:9153"
environment:
QDRANT_EXPORTER_QDRANT__NODES: '[{"url":"http://host.docker.internal:6333"}]'
extra_hosts:
- "host.docker.internal:host-gateway"
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml:ro
extra_hosts:
- "host.docker.internal:host-gateway"
grafana:
image: grafana/grafana:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: admin
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /etc/grafana/provisioning/dashboards/qdrant-overview.json
volumes:
- ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro
- ./dashboards:/etc/grafana/provisioning/dashboards:ro
depends_on:
- prometheusdocker compose up -dOpen Grafana at http://localhost:3000 (admin / admin). The dashboard is auto-provisioned.
# docker-compose.yml
services:
qdrant:
image: qdrant/qdrant:v1.16.3
restart: unless-stopped
ports:
- "6333:6333"
- "6334:6334"
qdrant-exporter:
image: baselhusam/qdrant-exporter
container_name: qdrant-exporter
restart: unless-stopped
ports:
- "9153:9153"
environment:
QDRANT_EXPORTER_QDRANT__NODES: '[{"url":"http://qdrant:6333"}]'
depends_on:
- qdrant
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml:ro
grafana:
image: grafana/grafana:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: admin
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /etc/grafana/provisioning/dashboards/qdrant-overview.json
volumes:
- ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro
- ./dashboards:/etc/grafana/provisioning/dashboards:ro
depends_on:
- prometheusdocker compose up -dIf you want the bundled prometheus.yml and Grafana provisioning files without writing them yourself, clone the repo and use the pre-built profiles:
git clone https://github.com/baselhusam/qdrant_exporter
cd qdrant_exporter
# Attach to an existing Qdrant on localhost:6333
docker compose --profile monitoring up -d
# Or spin up the full stack including Qdrant
docker compose --profile fullstack up -dOpen Grafana at http://localhost:3000 (admin / admin). The dashboard is auto-provisioned.
pip install qdrant-exporter
# point it at your Qdrant
qdrant-exporter --config qdrant-exporter.yamlThen scrape http://localhost:9153/metrics from your existing Prometheus.
| Service | URL | Notes |
|---|---|---|
| Grafana | http://localhost:3000 | admin / admin |
| Prometheus | http://localhost:9090 | |
| Exporter | http://localhost:9153/metrics | Prometheus scrape target |
| Qdrant | http://localhost:6333 | fullstack mode only |
┌─────────────┐ HTTP :6333 ┌──────────────────┐
│ Qdrant │ ◄────────────────── │ qdrant-exporter │
│ │ /telemetry │ │
│ │ /cluster └────────┬─────────┘
│ │ /collections │ :9153/metrics
└─────────────┘ │
┌────────▼─────────┐
│ Prometheus │ scrapes every 15s
└────────┬─────────┘
│ PromQL
┌────────▼─────────┐
│ Grafana │
└──────────────────┘
The exporter is pull-based — it queries Qdrant on every Prometheus scrape, not on a timer.
All metrics are prefixed with qdrant_exporter_.
| Metric | Labels | Description |
|---|---|---|
collection_vectors_total |
collection |
Total vectors |
collection_indexed_vectors_total |
collection |
Indexed vectors |
collection_points_total |
collection |
Total points |
collection_segments_total |
collection |
Segment count |
collection_status |
collection, status |
1 = active state (green/yellow/red/grey) |
collection_optimizer_status |
collection, status |
1 = active state (ok/error) |
shard_vectors_total |
collection, shard_id |
Vectors per shard |
shard_points_total |
collection, shard_id |
Points per shard |
shard_vectors_bytes |
collection, shard_id |
Vector storage size |
shard_payloads_bytes |
collection, shard_id |
Payload storage size |
shard_state |
collection, shard_id, state |
1 = active state |
| Metric | Labels | Description |
|---|---|---|
cluster_enabled |
— | 1 = distributed mode, 0 = single node |
cluster_peers_total |
— | Number of nodes in the cluster |
cluster_raft_term |
— | Current Raft election term |
cluster_raft_commit |
— | Last committed log index |
cluster_raft_pending_operations |
— | Ops not yet applied (should be ~0) |
cluster_is_leader |
— | 1 = this node is the Raft leader |
collection_shard_count |
collection |
Total shards across all peers |
collection_local_shard_points |
collection, shard_id |
Points in each local shard |
collection_local_shard_state |
collection, shard_id, state |
Local shard state |
collection_remote_shard_state |
collection, shard_id, peer_id, state |
Remote replica state |
collection_shard_transfers_active |
collection |
Ongoing shard migrations |
collection_resharding_operations_active |
collection |
Active resharding ops |
| Metric | Labels | Description |
|---|---|---|
search_count_total |
collection, search_type |
Searches per index path type |
search_fail_count_total |
collection, search_type |
Failed searches |
search_avg_duration_micros |
collection, search_type |
Average search latency (µs) |
optimizer_runs_total |
collection |
Completed optimization passes |
optimizer_runs_failed_total |
collection |
Failed optimization passes |
optimizer_avg_duration_micros |
collection |
Avg optimizer pass duration (µs) |
optimizer_active_total |
collection |
Currently running optimizer tasks |
collection_indexed_ratio |
collection |
Indexed vectors / total (0–1) |
collection_deleted_vectors_total |
collection |
Tombstoned vectors pending compaction |
collection_ram_bytes |
collection |
RAM used by all segments |
collection_disk_bytes |
collection |
Disk used by all segments |
search_type is one of: unfiltered_hnsw, unfiltered_plain, unfiltered_sparse, unfiltered_exact, filtered_plain, filtered_small_cardinality, filtered_large_cardinality, filtered_exact, filtered_sparse.
# qdrant-exporter.yaml
qdrant:
nodes:
- url: http://localhost:6333
api_key: your-key-here # omit if no auth configured
scrape_interval: 15 # seconds
telemetry_details_level: 3 # required for per-collection shard stats on Qdrant 1.16+
exporter:
port: 9153
log_level: info # debug | info | warning | error
metric_prefix: qdrant_exporterAll fields can be overridden with environment variables using the QDRANT_EXPORTER_ prefix and __ as the nested delimiter:
QDRANT_EXPORTER_QDRANT__NODES='[{"url":"http://localhost:6333","api_key":"secret"}]'
QDRANT_EXPORTER_EXPORTER__PORT=9153
QDRANT_EXPORTER_EXPORTER__LOG_LEVEL=debugPer-collection shard and storage metrics come from GET /telemetry?details_level=N. The legacy style details=level=3 often returns only number_of_collections, so nested collections.collections[] is missing and many qdrant_exporter_* series never appear. The exporter defaults to telemetry_details_level: 3; override via QDRANT_EXPORTER_QDRANT__TELEMETRY_DETAILS_LEVEL if needed.
The bundled Grafana Overview row includes panels that use native qdrant_* series from Qdrant’s own HTTP /metrics (for example high-level process stats). Configure Prometheus to scrape both Qdrant (:6333/metrics) and this exporter (:9153/metrics); see deploy/prometheus.yml for a sample.
The dashboard (dashboards/qdrant-overview.json) is auto-provisioned when using the Docker Compose setup. It has four rows:
- Overview — total collections, vectors, points, Qdrant version, cluster mode, memory, per-collection health tiles
- Search Performance — search rate (req/s) via
deriv()on cumulative-style gauges, avg latency by search type (ms), error rate, search count by type - Storage & Indexing — indexing ratio gauge (alerts below 1.0), RAM and disk per collection, optimizer run history, deleted vectors
- Cluster — cluster mode, Raft leader status, pending operations, peer count, shard transfer tracking
You can also import it directly from Grafana.com → or manually from dashboards/qdrant-overview.json.
Tested against Qdrant v1.13.0 – v1.17.1. The exporter performs a version check at startup and prints a compatibility assessment.
The minimum version where core telemetry fields are present is approximately v1.7.0. Older versions will work but some metrics may return 0.
Apache 2.0



