Skip to content

Quick start

First metrics in five minutes

Install, connect, verify, scrape. One static binary reads one PostgreSQL instance and answers on :9630. There is no agent to deploy, no sidecar to schedule, and nothing to compile.

You need a reachable PostgreSQL 10–19+ (or PgBouncer 1.8+) and permission to create a role in it

Still on PostgreSQL 9.1–9.6? Check the compatibility matrix first

The whole path

Five commands, in order

Each one is independently verifiable, so a failure tells you which half of the pipeline to look at.

  1. Install the binary

    A repository package on a long-lived host, or the release archive anywhere else.

    sudo apt install -y pg-exporter
  2. Create a monitoring role

    pg_monitor is a built-in role since PostgreSQL 10 and covers every read the default collectors make.

    GRANT pg_monitor TO monitor;
  3. Point it at the database

    The URL may come from a flag, the environment, or a secret file — never from a shell history you will forget.

    export PG_EXPORTER_URL='postgres://monitor:S3cret@localhost:5432/postgres'
  4. Parse, then run

    --dry-run prints the merged collector set and exits; without it the exporter listens on :9630.

    pg_exporter --dry-run && pg_exporter
  5. Confirm and scrape

    pg_up 1 means every layer works. Only then does the target belong in a Prometheus job.

    curl -s localhost:9630/metrics | grep '^pg_up '

Step 01

Install

On Linux amd64 the release archive is the shortest path. For managed packages, other platforms, containers, Pigsty, and source builds, use the download page.

VERSION=$(curl -fsSL https://api.github.com/repos/pgsty/pg_exporter/releases/latest | sed -n 's/.*"tag_name": "v\([^"]*\)".*/\1/p')
wget "https://github.com/pgsty/pg_exporter/releases/download/v${VERSION}/pg_exporter-${VERSION}.linux-amd64.tar.gz"
mkdir -p "pg_exporter-${VERSION}.linux-amd64"
tar -xf "pg_exporter-${VERSION}.linux-amd64.tar.gz" -C "pg_exporter-${VERSION}.linux-amd64"
sudo install "pg_exporter-${VERSION}.linux-amd64/pg_exporter" /usr/bin/
sudo install "pg_exporter-${VERSION}.linux-amd64/pg_exporter.yml" /etc/pg_exporter.yml

Confirm the installation:

pg_exporter --version
Prefer a package on a host you will keep

The RPM/DEB route adds an upgrade path, file ownership, /etc/default/pg_exporter, and a systemd unit that runs as the prometheus user. The archive gives you the binary and leaves the service definition to you.

Step 02

Create a monitoring user

A dedicated least-privilege role on the target instance. The built-in pg_monitor role, available since PostgreSQL 10, grants every read the default collectors need — and nothing else.

CREATE USER monitor WITH PASSWORD 'S3cret';
GRANT pg_monitor TO monitor;

Trying it out locally as a superuser such as postgres? Skip this step — but do not carry that shortcut into a host anyone else can reach.

Step 03

Run and verify

Parse the configuration first, then start for real. Both commands read the same URL, so a typo fails before anything listens on a port.

export PG_EXPORTER_URL='postgres://monitor:S3cret@localhost:5432/postgres'

pg_exporter --dry-run     # print the parsed collector config, then exit
pg_exporter               # start for real, listening on :9630 by default

With no URL at all, pg_exporter falls back to the local-first default postgresql:///?sslmode=disable, which fits running on the same host as PostgreSQL. The full precedence — --url › PG_EXPORTER_URL › PGURL › PG_EXPORTER_URL_FILE › default — is documented in the deployment guide.

Pull the metrics from another terminal:

curl -s http://localhost:9630/metrics | grep -E '^pg_(up|version|in_recovery) '

What a healthy target looks like

pg_up 1 means the whole pipeline works — the remaining 600+ metrics (pg_db_*, pg_table_*, pg_wal_*, …) all come from the declarative collector definitions in pg_exporter.yml. If pg_up is 0, restart with --log.level=debug and read the connection error.

curl -s localhost:9630/metrics
pg_up 1              # 1 when the target is reachable, 0 otherwise
pg_version 170000    # version in server_version_num format
pg_in_recovery 0     # 1 on replicas, 0 on primaries

Step 04

Hook into Prometheus

Add one scrape target. The exporter is pull-based and holds no queue, so nothing else has to change.

prometheus.yml
scrape_configs:
  - job_name: 'postgresql'
    scrape_interval: 15s
    static_configs:
      - targets: ['localhost:9630']

Collectors cache their results for their own ttl — most realtime collectors use ttl: 10. As long as the TTL stays below the scrape interval, every scrape gets fresh data while high-frequency scraping can never overwhelm the database. That is also why setting scrape_interval below the common TTLs is not recommended.

That is the whole path. For Grafana, reuse the PostgreSQL dashboards from Pigsty, or explore the live demo.

If something is off

Four symptoms, and where the answer lives

The exporter exposes the evidence needed to operate it, not just the database metrics it emits. Reach for /explain and /stat before reaching for the logs.

pg_up 0 — the connection fails

Restart with pg_exporter --log.level=debug and read the error it prints. It is almost always one of three things: a URL that names the wrong host, database, or user; a pg_hba.conf rule that does not admit the monitoring role from that address; or a network path that never reaches the port.

Some metrics never appear

Ask the exporter why. curl localhost:9630/explain prints every collector’s planning verdict — which branch was selected, which was skipped for a version gate, a role mismatch, a tag, or a failed predicate.

Read the API reference
One collector keeps failing

curl localhost:9630/stat returns per-collector error counters, durations, and cache state. A non-fatal collector fails in isolation: its siblings keep reporting, which is why a single broken query never takes the target down.

Read the API reference
Scrapes are slow

Find the slow collector in /stat, then either raise its ttl so its result is reused across scrapes or set skip: true if you do not need it. Collector cost is configuration, not a property of the binary.

Read the API reference
Are the management endpoints safe to expose?

/stat, /explain, and /reload describe and control the exporter. In production, protect them with --web.config.file for TLS and authentication, or keep the listener on a trusted network.

Read the API reference

Then what

Take it to production

Operate

Production deployment

PgBouncer targets, auto-discovery, credentials and TLS, systemd, Docker, and Kubernetes.

Read

Configure

Collector configuration

GAUGE, COUNTER, HISTOGRAM and LABEL semantics, TTL, tags, and version gates.

Read

Inspect

HTTP API

Health checks and primary/replica traffic routing through /up, /primary, and /replica.

Read

Reference

Bundled collectors

What the 58 shipped definition files actually measure, collector by collector.

Read

Harden

Security posture

Least-privilege roles, secret handling, TLS on both hops, and what the exporter never reads.

Read

Diagnose

Troubleshooting

A longer catalogue of failure modes, with the endpoint that identifies each one.

Read

Working end to end?Bring the rest of the stack — recording rules, alerts, and Grafana dashboards — or read how the collectors are written.