PO

PostgreSQL Exporter

Prometheus exporter for PostgreSQL server metrics

Monitoring & Status ★ 3.6k stars Easy setup Apache-2.0

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

MetricWatch forWhy
pg_up0 for 2 minExporter cannot connect: server down, auth broken, or pg_hba.conf changed
pg_stat_database_numbackends against pg_settings_max_connectionsabove 80 percentConnection exhaustion is the most common self-inflicted outage
pg_stat_replication_pg_wal_lsn_diffgrowingThe standby you plan to fail over to is stale
pg_database_size_bytesrate over 24 hCatches runaway tables and bloat before the disk does
pg_stat_database_deadlocksrate above 0Application bug, not a database bug, but you find it here first
pg_stat_activity_max_tx_durationabove 300 sLong 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