Skip to content

gurjarchetan/mongodb-exporter

Repository files navigation

mongodb-exporter

Production-grade Prometheus exporter for MongoDB, written in Go.

Go Version MongoDB License Docker Pulls Docker Image Size GHCR Stars Issues Grafana Dashboard

60+ metrics · 10 MB distroless image · TLS · Replica-set aware · Pre-built Grafana dashboard


Why another MongoDB exporter?

Most existing exporters are either unmaintained, bloated, or missing the metrics that matter for production. This one is:

  • Accurate — metrics sourced directly from serverStatus, dbStats, replSetGetStatus, and system.profile; no scraping shell commands
  • Lightweight — single static binary, ~10 MB distroless Docker image, no shell, no package manager
  • Production-ready — TLS, graceful shutdown, per-scrape timeout, panic recovery per collector, configurable cardinality controls
  • Observable out of the box — ships a 45-panel Grafana dashboard covering operations, latency, connections, WiredTiger internals, replication, database statistics, and slow-query profiler

Table of Contents


Quick Start

git clone https://github.com/gurjarchetan/mongodb-exporter
cd mongodb-exporter
docker compose up -d

Or pull the pre-built image directly:

# GitHub Container Registry (primary)
docker pull ghcr.io/gurjarchetan/mongodb-exporter:v1.0.0

# Docker Hub (mirror)
docker pull chetangurjar/mongodb-exporter:v1.0.0

GHCR: https://github.com/gurjarchetan/mongodb-exporter/pkgs/container/mongodb-exporter Docker Hub: https://hub.docker.com/r/chetangurjar/mongodb-exporter

Open Grafana at http://localhost:3000 (admin / admin) — the MongoDB — Production Overview dashboard appears within ~30 seconds.

Service URL Credentials
Grafana http://localhost:3000 admin / admin
Prometheus http://localhost:9090
Exporter http://localhost:9216/metrics
MongoDB mongodb://localhost:27017 no auth (dev)

Docker Compose — Full Dev Stack

The full dev stack spins up MongoDB 8.0 + Exporter + Prometheus + Grafana in a single command, pre-seeded with demo data and profiling enabled.

# Option A — root docker-compose.yml (shorthand)
docker compose up -d

# Option B — explicit path
docker compose -f deploy/docker-compose/dev-stack.yml up -d

Both compose files are equivalent. The deploy/docker-compose/dev-stack.yml version is intended for CI and cross-project usage; it references all assets via relative paths from the repository root.

What it provisions

Container Description
mongodb MongoDB 8.0, single-node replica set rs0, profiling enabled
mongo-init One-shot: initialises replica set, seeds demo databases
mongodb-exporter This project — built from source via Dockerfile
prometheus Scrapes exporter every 15 s, 30-day retention
grafana Auto-provisioned datasource + 45-panel dashboard

Demo databases (seeded by scripts/seed-data.js)

  • ecommerce — products, orders, users
  • analytics — event stream
  • iot_sensors — time-series sensor readings

Tear down

docker compose down          # stop, keep volumes
docker compose down -v       # stop + delete all data

Docker Compose — Exporter Only

Use this when you already have MongoDB (and Prometheus/Grafana) running.

# Set your MongoDB URI
export MONGODB_URI="mongodb://exporter:secret@your-mongo:27017/admin?authSource=admin"

docker compose -f deploy/docker-compose/exporter-only.yml up -d

Or create a .env file next to the compose file:

MONGODB_URI=mongodb://exporter:secret@your-mongo:27017/admin?authSource=admin

The exporter is then available at http://<host>:9216/metrics.

Image — by default exporter-only.yml pulls chetangurjar/mongodb-exporter:latest from Docker Hub. To build from source, uncomment the build: block inside the file.


Binary / Bare Metal

# Build
make build

# Run
./bin/mongodb-exporter \
  --mongodb.uri="mongodb://exporter:secret@localhost:27017/admin?authSource=admin" \
  --collector.dbstats=true \
  --collector.replication=true \
  --collector.profile=true \
  --web.listen-address=:9216

Pre-built binaries for Linux/macOS/Windows (amd64 + arm64) are available on the Releases page.


Configuration

All flags can also be set via environment variables. CLI flag --foo.bar maps to env var FOO_BAR (upper-case, dots → underscores). The most common env var is MONGODB_URI.

Flag Default Description
--mongodb.uri mongodb://localhost:27017 MongoDB connection URI
--web.listen-address :9216 HTTP listen address
--web.telemetry-path /metrics Metrics endpoint path
--scrape.timeout 10s Per-scrape MongoDB timeout
--collector.dbstats true Per-database storage statistics
--collector.replication true Replica-set health, lag, oplog metrics
--collector.currentop false Currently-running operations (may be expensive on busy servers)
--collector.profile false Slow queries from system.profile — see Enabling the Query Profiler
--collector.collstats false Per-collection statistics (high cardinality — use with care)
--mongodb.tls false Enable TLS for MongoDB connection
--mongodb.tls-ca "" Path to CA certificate file
--log.level info Log level: debug | info | warn | error
--version Print version information and exit

Enabling the Query Profiler

The profiler collector (--collector.profile=true) reads system.profile on every application database and exposes three metrics:

Metric Description
mongodb_profiling_level{database} Current profiler level: 0 = off, 1 = slow ops only, 2 = all ops
mongodb_slow_queries_total{database,collection,op} Query count per DB/collection/op type since last scrape
mongodb_slow_query_millis_total{database,collection,op} Total milliseconds spent, per DB/collection/op

Step 1 — Enable profiling in MongoDB

// Level 1: log ops slower than 100 ms (recommended for production)
db.getSiblingDB("mydb").setProfilingLevel(1, { slowms: 100 })

// Level 2: log all ops (development / debugging only)
db.getSiblingDB("mydb").setProfilingLevel(2)

// Check current level
db.getSiblingDB("mydb").getProfilingStatus()
// → { was: 1, slowms: 100, ... }

Step 2 — Make it persistent across restarts

Pass --profile and --slowms to mongod so the setting survives pod/container restarts:

# docker-compose.yml / Kubernetes pod spec
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--profile", "1", "--slowms", "100"]

Step 3 — Enable the collector

./bin/mongodb-exporter --collector.profile=true ...

Grafana — Profiler Level panel

The Slow Queries (Profiler) row in the dashboard shows a colour-coded status bar per database:

Colour Value Meaning
🔴 Red 0 Profiling is OFF — no slow-query data will be collected
🟡 Yellow 1 Slow ops — only queries exceeding slowms are logged
🟢 Green 2 All ops — every query is logged (use for debugging only)

Metrics Reference

Always on

Metric Type Labels Description
mongodb_up Gauge 1 if reachable, 0 if not
mongodb_scrape_duration_seconds Gauge Wall-clock scrape time
mongodb_uptime_seconds Gauge MongoDB process uptime
mongodb_connections_current Gauge Open connections
mongodb_connections_available Gauge Available connection slots
mongodb_connections_active Gauge Active (in-use) connections
mongodb_connections_created_total Counter Total connections ever made
mongodb_opcounters_total Counter type Op counts (insert/query/update/delete/command/getmore)
mongodb_opcounters_repl_total Counter type Replicated op counts
mongodb_op_latency_micros_total Counter type Cumulative latency µs (read/write/command)
mongodb_op_latency_ops_total Counter type Op count for latency histogram
mongodb_mem_resident_bytes Gauge Resident (RSS) memory
mongodb_mem_virtual_bytes Gauge Virtual memory
mongodb_network_bytes_in_total Counter Network bytes received
mongodb_network_bytes_out_total Counter Network bytes sent
mongodb_network_requests_total Counter Total network requests
mongodb_asserts_total Counter type Assertion counts by type
mongodb_wiredtiger_cache_bytes Gauge type WiredTiger cache (current/dirty)
mongodb_wiredtiger_cache_max_bytes Gauge WiredTiger cache configured maximum
mongodb_wiredtiger_cache_io_bytes_total Counter direction Cache I/O bytes (read/written)
mongodb_wiredtiger_cache_evicted_pages_total Counter type Cache page evictions
mongodb_wiredtiger_concurrent_transactions_out Gauge Tickets in use
mongodb_wiredtiger_concurrent_transactions_available Gauge Tickets available
mongodb_wiredtiger_transactions_committed_total Counter WT committed transactions
mongodb_wiredtiger_transactions_rolled_back_total Counter WT rolled-back transactions
mongodb_transactions_current Gauge state Multi-doc transactions by state
mongodb_transactions_total Counter state Multi-doc transaction outcomes
mongodb_metrics_document_total Counter state Document ops (inserted/returned/updated/deleted)
mongodb_metrics_cursor_open Gauge state Open cursors
mongodb_metrics_cursor_timed_out_total Counter Timed-out cursors
mongodb_metrics_query_executor_scanned_total Counter Index entries scanned
mongodb_metrics_query_executor_scanned_objects_total Counter Documents scanned
mongodb_metrics_collection_scans_total Counter type Collection scans (full)
mongodb_global_lock_current_queue Gauge type Lock queue depth
mongodb_flow_control_is_lagged Gauge 1 if flow-controlled/throttled

--collector.dbstats

Metric Labels Description
mongodb_db_data_size_bytes database Uncompressed data size
mongodb_db_storage_size_bytes database On-disk storage size
mongodb_db_index_size_bytes database Total index size
mongodb_db_total_size_bytes database Data + index total
mongodb_db_objects_total database Document count
mongodb_db_collections_total database Collection count
mongodb_db_indexes_total database Index count
mongodb_db_avg_obj_size_bytes database Average document size

--collector.replication

Metric Labels Description
mongodb_repl_member_health member 1 if member is up
mongodb_repl_member_state member, state 1 for current state
mongodb_repl_lag_seconds member Replication lag vs primary
mongodb_repl_member_uptime_seconds member Member uptime
mongodb_oplog_max_size_bytes Configured oplog size
mongodb_oplog_used_bytes Current oplog usage
mongodb_oplog_time_diff_seconds Oplog window (oldest → newest entry)

--collector.profile

Metric Labels Description
mongodb_profiling_level database Current profiler level (0 / 1 / 2)
mongodb_slow_queries_total database, collection, op Queries logged since last scrape
mongodb_slow_query_millis_total database, collection, op Milliseconds spent in logged queries

Grafana Dashboard

The included dashboard (monitoring/grafana/dashboards/mongodb.json) has 53 panels organised into collapsible rows:

Row Panels Key metrics
Status 8 stat panels UP/DOWN · Uptime · Ops/s · Open/Active Connections · Resident memory · WiredTiger cache % · Replication lag
Operations & Documents 2 timeseries Opcounters rate · Document ops rate
Latency 3 timeseries Read / Write / Command avg latency (µs)
Connections & Global Lock 3 timeseries Connection pool · Lock queue · Open cursors
Memory & WiredTiger Cache 4 panels Resident/virtual memory · Cache utilisation gauge · Cache bytes breakdown
WiredTiger Internals 3 timeseries Concurrent ticket usage · Cache evictions & I/O · Transactions & checkpoints
Network 2 timeseries Throughput in/out · Requests/s
Replication 6 panels Oplog window stat · Oplog used % gauge · Oplog bytes · Apply ops/s · RS members table · Apply buffer
Database Statistics 3 panels Storage size bar gauge · Documents bar gauge · Detail table with colour-coded Total Size
Query Efficiency 2 timeseries Scanned vs returned ratio · Collection scans/sorts
Slow Query Profiler 5 panels Profiler level per database (colour-coded OFF/SLOW OPS/ALL OPS) · State-timeline history · Slow query rate · Avg duration
Asserts & Transactions 2 timeseries Assert rate by type · Multi-doc transaction states

Screenshots

Status · Operations · Latency Status and Operations

Connections · Memory · WiredTiger Cache Connections and WiredTiger Cache

WiredTiger Internals · Network WiredTiger Internals and Network

Replication · Database Statistics Replication and Database Statistics

Slow Query Profiler — profiler level per database (colour-coded), state-timeline history, slow query rate & avg duration Slow Query Profiler

Asserts · Multi-document Transactions Asserts and Transactions

Importing manually

  1. Open Grafana → Dashboards → Import
  2. Upload monitoring/grafana/dashboards/mongodb.json
  3. Select your Prometheus datasource
  4. Click Import

Regenerating the dashboard

The dashboard JSON is generated by generate_dashboard.py (no external dependencies):

python3 generate_dashboard.py
# OK: 53 panels written to monitoring/grafana/dashboards/mongodb.json

Prometheus Scrape Config

scrape_configs:
  - job_name: mongodb
    scrape_interval: 15s
    scrape_timeout: 10s
    static_configs:
      - targets:
          - mongodb-exporter:9216   # adjust host/port
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance

MongoDB User (Minimal Privileges)

Create a dedicated monitoring user with the least-privilege roles:

db.getSiblingDB("admin").createUser({
  user: "mongodb_exporter",
  pwd: "STRONG_RANDOM_PASSWORD",
  roles: [
    { role: "clusterMonitor", db: "admin" },
    { role: "read",           db: "local" }   // required for oplog metrics
  ]
})

If --collector.profile=true is enabled, the user also needs read on each profiled database:

db.getSiblingDB("myapp").grantRolesToUser("mongodb_exporter", [
  { role: "read", db: "myapp" }
])

Connection URI:

mongodb://mongodb_exporter:STRONG_RANDOM_PASSWORD@localhost:27017/admin?authSource=admin

TLS

./bin/mongodb-exporter \
  --mongodb.uri="mongodb://exporter:pass@mongo.internal:27017/admin" \
  --mongodb.tls=true \
  --mongodb.tls-ca=/etc/ssl/certs/mongo-ca.crt

In Docker Compose:

mongodb-exporter:
  command:
    - --mongodb.tls=true
    - --mongodb.tls-ca=/certs/ca.crt
  volumes:
    - /path/to/ca.crt:/certs/ca.crt:ro

TLS 1.2 is the minimum enforced version.


Kubernetes & Helm

Kubernetes manifests

# Edit deploy/kubernetes/deployment.yaml to set your MONGODB_URI secret, then:
kubectl apply -f deploy/kubernetes/

Helm chart

helm install mongodb-exporter ./deploy/helm \
  --set mongodb.uri="mongodb://exporter:pass@mongo:27017/admin?authSource=admin" \
  --set collector.profile=true

Development

Prerequisites

  • Go 1.21+
  • Docker + Docker Compose v2
  • golangci-lint (optional, for linting)

Common commands

make build        # compile binary → bin/mongodb-exporter
make test         # go test -race ./...
make lint         # golangci-lint run
make docker-build # build Docker image
make up           # start full observability stack
make down         # stop stack (keep data)
make logs         # tail all container logs
make scrape       # curl http://localhost:9216/metrics

Adding a new collector

  1. Create collector/my_collector.go implementing prometheus.Collector
  2. Register it in exporter/exporter.go (guarded by a config flag if optional)
  3. Add a CLI flag in main.go
  4. Add metrics to the Metrics Reference table above and to the dashboard generator

Project structure

.
├── collector/          # Individual metric collectors
│   ├── server_status.go    # 60+ metrics from serverStatus
│   ├── db_stats.go         # Per-database storage stats
│   ├── replication.go      # Replica-set health, oplog
│   ├── profile.go          # Slow-query profiler + profiling level
│   ├── current_op.go       # Running operations
│   └── coll_stats.go       # Per-collection stats
├── exporter/           # Wires collectors, owns MongoDB client
├── deploy/
│   ├── docker-compose/
│   │   ├── dev-stack.yml       # Full stack (MongoDB + Exporter + Prometheus + Grafana)
│   │   └── exporter-only.yml   # Exporter only (connect to existing MongoDB)
│   ├── kubernetes/         # Deployment, Service, ConfigMap manifests
│   └── helm/               # Helm chart
├── monitoring/
│   ├── prometheus.yml
│   └── grafana/
│       ├── dashboards/mongodb.json   # 45-panel production dashboard
│       └── provisioning/
├── scripts/
│   ├── mongo-init.js     # Replica set init, user creation
│   └── seed-data.js      # Demo data (ecommerce, analytics, iot_sensors)
├── generate_dashboard.py # Dashboard JSON generator
├── Dockerfile            # Multi-stage distroless build
├── docker-compose.yml    # Root-level shorthand for dev stack
└── Makefile

Security

  • Least privilege — only clusterMonitor + read on local; no write access required
  • Credentials via env — pass MONGODB_URI as an environment variable or Kubernetes Secret; never in CLI flags visible via ps
  • Distroless image — no shell, no package manager, minimal attack surface; runs as nonroot (UID 65534) with a read-only filesystem
  • TLS — 1.2+ enforced when --mongodb.tls is set; custom CA certificate supported
  • No cardinality explosioncollstats collector is off by default; profile collector hard-caps at 10 000 entries per scrape

Contributing

Contributions are welcome! Please:

  1. Open an issue describing the problem or feature before sending a large PR
  2. Follow existing code style (gofmt, golangci-lint clean)
  3. Add or update the relevant Metrics Reference table entries
  4. Keep the dashboard generator (generate_dashboard.py) in sync with new metrics
  5. Ensure go test -race ./... passes

Built with ❤️ for the MongoDB + Prometheus community

About

Production-grade Prometheus exporter for MongoDB — 60+ metrics, 53-panel Grafana dashboard, distroless Docker image, profiler support, TLS, Helm chart

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors