Exporters explained: Monitoring best practices explained
When you're running infrastructure, not everything speaks Prometheus natively. Your Postgres database, Redis cache, Nginx reverse proxy, and dozens of other systems emit metrics in their own formats—or don't expose them at all. That's where exporters step in. An exporter is a lightweight application that scrapes or listens to your systems, translates their metrics into the Prometheus format, and exposes them on an HTTP endpoint for Prometheus to scrape.
Understanding exporters is essential for building a production monitoring stack. In this guide, we'll walk through what exporters do, which ones you'll likely use, deployment patterns that work well, and the cardinality traps that catch most teams.
What they do — Translate system stats into Prometheus metrics
An exporter's job is straightforward: connect to a system, read its current state, and convert it into Prometheus' text-based exposition format. Prometheus expects metrics in a specific format: a metric name, optional labels, and a value, one per line.
For example, your Postgres database might report active connections through its internal system views. The Postgres exporter queries the database, pulls that number, and outputs it as:
pg_stat_activity_count{datname="production",state="active"} 42Prometheus scrapes this endpoint periodically (typically every 15 or 30 seconds), stores it in its time-series database, and you can now graph it, alert on it, and correlate it with other metrics.
The exporter sits between your system and Prometheus. It handles authentication to the system being monitored, normalizes metrics into Prometheus format, and handles errors gracefully. Without exporters, you'd need to modify every application and infrastructure component to speak Prometheus—a task that's often impossible when you don't control the source code.
Common exporters — Node, Postgres, Redis, Nginx and Blackbox
Most production teams use a core set of exporters. Understanding their scope and what they expose is crucial for building reliable dashboards and alerts.
Node Exporter
The Node Exporter monitors host-level metrics: CPU, memory, disk, network, and system calls. It reads from Linux procfs and sysfs interfaces. You run one Node Exporter per server.
Key metrics it exposes:
node_cpu_seconds_total— CPU time spent in different modes (user, system, iowait)node_memory_MemAvailable_bytes— Available RAMnode_disk_io_time_seconds_total— Disk I/O timenode_network_receive_bytes_total— Bytes received per interface
Node Exporter generates hundreds of metrics per host. In a 50-node cluster, you're easily looking at 50,000+ time series from Node Exporter alone. That's manageable but worth noting when sizing your Prometheus instance.
Postgres Exporter
The Postgres Exporter connects to your database and runs SQL queries to fetch metrics. It exposes query performance, replication lag, connection counts, cache hit ratios, and table/index sizes.
Example use: you're troubleshooting slow queries. Node Exporter tells you the host isn't under memory pressure, but Postgres Exporter shows pg_stat_statements_mean_exec_time spiking on specific queries. Now you know to investigate query plans, not hardware.
The exporter needs database credentials and appropriate roles. A typical setup runs the exporter as a separate service that connects to the database on localhost.
Redis Exporter
Redis Exporter pulls stats from the Redis server: memory usage, connected clients, commands executed, evictions, and replication status. Redis already has good observability via the INFO command, so the exporter mainly formats that into Prometheus metrics.
You'll watch metrics like redis_memory_used_bytes, redis_evicted_keys_total, and redis_connected_clients. High eviction rates signal that your cache is undersized. Rising client counts might indicate connection leaks in your application.
Nginx Exporter
Nginx doesn't emit metrics natively. The Nginx Exporter reads from Nginx's stub status endpoint (you enable this in your Nginx config) or parses access logs. It exposes request counts, response codes, connection states, and upstream latencies.
Enable stub status in Nginx:
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status;
access_log off;
}
}Then run the exporter pointing to that endpoint. You'll get metrics like nginx_requests_total, nginx_http_requests_total{status="200"}, and nginx_upstream_requests_total.
Blackbox Exporter
Blackbox Exporter is different. Instead of connecting to a system to pull metrics, it probes endpoints and reports whether they're up or down, how long they took to respond, and whether SSL certificates are valid.
You give it URLs to test:
- targets:
- https://api.example.com/health
- https://payments.example.com/status
module: http_2xxBlackbox Exporter makes HTTP requests, records latency, and exposes metrics like probe_success (0 or 1), probe_duration_seconds, and probe_http_status_code. It's your window into external dependencies and production APIs.
Run alongside — Sidecars or separate containers
You have two main deployment patterns for exporters: sidecar and separate service. The choice depends on your architecture and operational constraints.
Sidecar pattern
Run the exporter in the same pod as the service it monitors. Postgres and the Postgres Exporter share a pod. This tightly couples them and simplifies networking (the exporter connects to localhost).
Docker Compose example:
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
postgres_exporter:
image: prometheuscommunity/postgres-exporter
environment:
DATA_SOURCE_NAME: "postgresql://postgres:secret@localhost:5432/postgres?sslmode=disable"
ports:
- "9187:9187"
depends_on:
- postgresPrometheus scrapes the exporter at localhost:9187. If Postgres goes down, the sidecar goes down with it, and Prometheus sees the scrape fail. That's often the desired behavior—if the service is unavailable, you want to know that monitoring is also unavailable.
Separate service pattern
Run the exporter as its own deployment or container, separate from the monitored service. Node Exporter and Blackbox Exporter typically run this way. Node Exporter runs on every host independently. Blackbox Exporter might run on dedicated probe servers.
This pattern gives you flexibility: you can restart the exporter without affecting the monitored service, you can scale them independently, and you can run multiple exporters pointing to the same service (for redundancy or multi-region setups).
Kubernetes example:
apiVersion: v1
kind: Service
metadata:
name: postgres-exporter
spec:
selector:
app: postgres-exporter
ports:
- port: 9187
targetPort: 9187
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-exporter
spec:
replicas: 1
selector:
matchLabels:
app: postgres-exporter
template:
metadata:
labels:
app: postgres-exporter
spec:
containers:
- name: exporter
image: prometheuscommunity/postgres-exporter
env:
- name: DATA_SOURCE_NAME
value: "postgresql://postgres:secret@postgres-db:5432/postgres?sslmode=disable"
ports:
- containerPort: 9187Choose the sidecar pattern when the exporter and service are tightly coupled and live and die together. Choose the separate service pattern for infrastructure-level exporters (Node Exporter, Blackbox Exporter) and when you need independent scaling or reliability.
Check cardinality — Some exporters expose a lot of series
Cardinality is the number of unique time series an exporter exposes. High cardinality kills your Prometheus instance. It causes memory bloat, slow query performance, and runaway storage costs.
Node Exporter is generally safe—tens of metrics per host, scales linearly. But some exporters are cardinality minefields.
The cardinality problem
Postgres Exporter can expose per-table metrics: pg_table_total_relation_size_bytes{schemaname="public",tablename="orders"}. If you have 1,000 tables, that's 1,000 distinct series for that one metric. Multiply that by dozens of table-level metrics and suddenly you've added millions of series.
Redis Exporter exposes per-key metrics if configured incorrectly. If you're tracking keyspace stats with high cardinality keys (timestamps in key names, user IDs, etc.), the exporter will create a new series for each unique key. That's a hard no.
Controlling cardinality
Most exporters let you disable or filter metrics. The Postgres Exporter accepts a --disable-default-metrics flag and allows you to specify which tables to monitor via configuration files.
Example Postgres Exporter config limiting metrics:
pg_stat_statements_inplace:
query: "SELECT pg_stat_statements(true)::text"
ttl: 10
master: true
metrics:
- queryid:
usage: "LABEL"
description: "Query ID"
- query:
usage: "LABEL"
description: "Query"
- calls:
usage: "COUNTER"
description: "Calls"
- mean_exec_time:
usage: "GAUGE"
description: "Mean execution time"Always audit your exporters' output before deploying to production. Run the exporter in a test environment and check how many metrics it exposes:
curl http://localhost:9187/metrics | grep -c "^postgres_" If it's producing tens of thousands of metrics when you expect hundreds, disable unnecessary collectors before rolling out.
Common mistakes
Running exporters without filtering. You inherit the exporter's defaults and suddenly Prometheus is storing millions of series. Always review what each exporter exposes and disable high-cardinality metrics you don't need.
Not thinking about exporter reliability. If an exporter crashes or hangs, Prometheus either times out scrapes or you get gaps in your metrics. Run exporters as systemd services, Kubernetes deployments, or managed containers with restart policies. Monitor the exporters themselves.
Mixing authentication methods. Some exporters need database credentials, others need API keys. Store these securely (Kubernetes secrets, environment variables, credential files with restricted permissions). Never hardcode them in Docker images or config files checked into Git.
Assuming one exporter per instance. You can run multiple exporters on the same host and multiple exporters pointing to the same service. Plan your port assignments and discovery mechanisms accordingly.
Ignoring scrape latency. Some exporters are slow. Postgres Exporter might take 2-3 seconds to query system tables on a large database. If your scrape interval is 15 seconds and each scrape takes 5 seconds, you'll start missing scrapes. Monitor scrape duration in Prometheus and adjust intervals or exporter configuration.
Key takeaways
- Exporters translate third-party system metrics into Prometheus format; use them for systems you don't control.
- Deploy exporters as sidecars when tightly coupled with a service, or as separate services for infrastructure-level monitoring.
- Node, Postgres, Redis, Nginx, and Blackbox exporters cover most production needs; understand what each exposes.
- Always audit exporter cardinality before production. Disable high-cardinality collectors to avoid bloating your Prometheus instance.
- Run exporters with restart policies and monitor their health; a dead exporter leaves blind spots in your observability.
- Store exporter credentials securely; never embed them in images or config.






Comments
Loading comments…