PostgreSQL Exporter
Prometheus exporter for PostgreSQL server metrics
PostgreSQL Exporter is the Prometheus community exporter that collects statistics from a PostgreSQL server, including connections, replication lag, table sizes, and query activity. It supports custom queries and is widely used for production database monitoring.
Key features
- Replication and connection metrics
- Custom query support
- Multi-database scraping
- Prometheus community maintained
Pros & cons
Strengths
- Battle-tested in production
- Rich default metrics
Trade-offs
- No UI of its own
- Needs Grafana for graphs
PostgreSQL Exporter replaces
Last reviewed Sep 13, 2026 · 728 words
Most broken postgres_exporter installs I have looked at had the same fault: the exporter was connecting as the postgres superuser, or as an application user that could not read pg_stat_replication, so either half the dashboard was blank or the security review failed. Create a dedicated role, grant it pg_monitor, and both problems go away:
CREATE USER exporter WITH PASSWORD 'long-random-string';
GRANT pg_monitor TO exporter;
GRANT CONNECT ON DATABASE postgres TO exporter;
pg_monitor is a built-in role since PostgreSQL 10 that reads every statistics view the exporter wants and nothing it does not. Do that first; the rest of this guide is about which of the several hundred resulting series to actually watch.
The connection string is the whole configuration
The exporter takes DATA_SOURCE_NAME (or the newer DATA_SOURCE_URI plus separate user and password variables) and listens on port 9187. A working Compose service:
services:
postgres-exporter:
image: quay.io/prometheuscommunity/postgres-exporter:latest
environment:
- DATA_SOURCE_NAME=postgresql://exporter:long-random-string@db:5432/postgres?sslmode=disable
ports:
- "9187:9187"
restart: unless-stopped
Scraping one exporter per PostgreSQL server is the normal shape. Newer releases also support a multi-target probe endpoint, so one exporter can be pointed at several servers the way Redis Exporter can, using the same relabelling pattern in Prometheus. By default the exporter auto-discovers every database on the server and reports per-database stats; --auto-discover-databases and --exclude-databases let you trim that on a server with 40 databases, which otherwise pushes cardinality up fast.
Six series worth an alert, in order
| Metric | Watch for | Why |
|---|---|---|
pg_up | 0 for 2 min | Exporter cannot connect: server down, auth broken, or pg_hba.conf changed |
pg_stat_database_numbackends against pg_settings_max_connections | above 80 percent | Connection exhaustion is the most common self-inflicted outage |
pg_stat_replication_pg_wal_lsn_diff | growing | The standby you plan to fail over to is stale |
pg_database_size_bytes | rate over 24 h | Catches runaway tables and bloat before the disk does |
pg_stat_database_deadlocks | rate above 0 | Application bug, not a database bug, but you find it here first |
pg_stat_activity_max_tx_duration | above 300 s | Long transactions block vacuum and inflate everything else |
Transaction ID wraparound is the seventh and the scariest, because it stops writes entirely, but it takes months to approach on a small database. Expose the database age with a custom query (below), alert at 1 billion, and forget about it.
Custom queries are the reason to pick this exporter
The default collectors cover the pg_stat_* views. Anything about your schema (rows in a job queue, oldest unprocessed order, size of one hot table) goes in a custom queries YAML file, mounted and pointed at with --extend.query-path:
job_queue:
query: "SELECT status, count(*) AS count FROM jobs GROUP BY status"
metrics:
- status:
usage: "LABEL"
- count:
usage: "GAUGE"
description: "Jobs by status"
The exporter runs each query on every scrape, so keep them cheap (indexed, no sequential scans on big tables) or lengthen the scrape interval for that job. The project has been moving from this file-based mechanism towards built-in collectors, so check the flag name against the release you pull. This is the feature that separates a database probe from a business probe, and it is why I run this over a generic SQL exporter.
Graphs live elsewhere
The listed cons are accurate and shared by every Prometheus exporter: there is no UI, no alerting and no storage in the binary. The Grafana dashboard usually imported is ID 9628, which is dated but covers connections, transactions, locks and cache hit ratio out of the box; Grafana plus Prometheus is the assumed stack, and the homelab monitoring and uptime piece covers getting that far. Against pganalyze, the hosted rival, you lose query-plan analysis and index recommendations and keep your query text on your own machine; for most self-hosters that is the right trade.
What I'd do
Create the exporter role with pg_monitor, run the official image next to each PostgreSQL server, alert on the six series above, and add one custom query per application for whatever table tells you that app is healthy. Import dashboard 9628 as a starting point and expect to replace half its panels within a month. Twenty minutes of setup, and the one time a connection leak fills max_connections you will know 30 minutes before the users do.
Similar monitoring & status apps
Uptime Kuma
Monitoring & StatusEasy self-hosted uptime monitoring tool
Replaces Pingdom, UptimeRobot
Netdata
Monitoring & StatusReal-time per-second infrastructure monitoring
Replaces Datadog, New Relic
Grafana
Monitoring & StatusOpen observability dashboards and visualization
Replaces Datadog
Prometheus
Monitoring & StatusMetrics-based monitoring and alerting toolkit
Replaces Datadog
Glances
Monitoring & StatusCross-platform system monitoring at a glance
Replaces Datadog
InfluxDB
Monitoring & StatusPurpose-built time series database for metrics and events
Replaces Datadog, AWS Timestream