cloudparts

pgwatch

A PostgreSQL-only monitoring collector from CYBERTEC: metrics are plain SQL queries run on a schedule, stored in Postgres, TimescaleDB or Prometheus, and shown in ready-made Grafana dashboards.

What it is
Metrics
Type
Open source
Made by
CYBERTEC PostgreSQL International GmbH
License
BSD 3-Clause
Facts checked
23 October 2026

What it is

pgwatch is a single Go daemon. It reads a list of sources (databases to watch) and a set of metric definitions, connects to each database, runs the queries on their intervals and writes the results to one or more sinks. A metric is just a named SQL query that returns an epoch_ns column plus whatever columns you want stored, with separate versions per PostgreSQL major version and for primaries or standbys; columns named tag_* become indexed tags. A preset maps metric names to intervals, for example basic or exhaustive, so many databases can share one profile. Sources and metrics live either in a small PostgreSQL schema, edited through the built-in web UI, or in YAML files. A source can be one database, every database on an instance (continuous discovery), a Patroni cluster found through etcd, PgBouncer, Pgpool-II, or, since v6, an existing Prometheus exporter endpoint.

Sinks are PostgreSQL 14 or later (optionally with TimescaleDB compression), a Prometheus endpoint for scraping, a JSON file, or your own gRPC server. With the Postgres sink, pgwatch partitions the tables (daily by default) and deletes data older than 14 days unless told otherwise. Grafana does the visualising: pgwatch ships dashboards for health checks, top statements, locks, replication and more. On the monitored side it asks for little: a role with pg_monitor, pg_stat_statements for the query dashboards, and PL/Python helper functions only for OS metrics such as CPU load, which it does not create unless you pass --create-helpers. pgwatch is free under the BSD 3-Clause licence; CYBERTEC sells support for it. v6.0 came out in September 2026.

Why teams pick it

  • Every metric is an SQL query in YAML, so adding one means writing a SELECT, not an exporter
  • Stores metrics in plain PostgreSQL or TimescaleDB, so you can query months of history with SQL as well as in Grafana
  • Finds databases on its own: all databases on an instance, or all members of a Patroni cluster
  • Asks for little on monitored servers: a pg_monitor role, no agent and no required extension

Watch out for

  • PostgreSQL only: for hosts and other services it can at most scrape an existing Prometheus exporter (since v6)
  • Most query-level panels stay empty without pg_stat_statements, which needs shared_preload_libraries and a server restart
  • OS metrics such as CPU and disk need PL/Python helper functions installed as superuser in each database, which managed services usually don’t allow
  • Fast-moving: major versions 4, 5 and 6 arrived within a year, after the move from pgwatch2 dropped its InfluxDB and Graphite storage

Alternatives

What all metrics have in common →Compare side by side →

How it fits with other technologies

A custom metric and preset in YAML, one source, two sinks · bash

cat > metrics.yaml <<'EOF'
metrics:
  connections:
    sqls:
      11: |                  # this version runs on PostgreSQL 11 and later
        select /* pgwatch_generated */
          (extract(epoch from now()) * 1e9)::int8 as epoch_ns,
          count(*)::int8 as value
        from pg_stat_activity;
    gauges: ['*']            # goes up and down, so not a Prometheus counter
    is_instance_level: true
presets:
  tiny:
    metrics:
      connections: 30        # seconds
EOF

cat > sources.yaml <<'EOF'
- name: prod-main
  kind: postgres
  conn_str: postgresql://pgwatch@db1:5432/app
  preset_metrics: tiny
  is_enabled: true
EOF

pgwatch --sources=sources.yaml --metrics=metrics.yaml \
  --sink=postgresql://pgwatch@localhost:5432/metrics \
  --sink=prometheus://:9187

See it in a real stack

No architecture uses pgwatch yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2016–2026

Kaarel Moppel started the project in 2016, and it was first released in 2017 as pgwatch2, under CYBERTEC’s GitHub organisation. The idea, in the project’s words, was to do the minimal work needed and glue together proven pieces: a collector that ran SQL queries, an existing database to keep the numbers (PostgreSQL, InfluxDB, Graphite or Prometheus) and Grafana for the dashboards.

Pavlo Golub then rewrote the collector as a new codebase. pgwatch 3.0.0 came out in December 2024 and the pgwatch2 repository was archived the same month. The rewrite narrowed storage to PostgreSQL, TimescaleDB, Prometheus, JSON files and a gRPC interface for anything else, and kept configuration in either Postgres or YAML. Releases have been frequent since: 4.0 in September 2025, 5.0 in February 2026 and 6.0 in September 2026, which added Prometheus exporters as sources, rebuilt every dashboard and changed the collector to run one goroutine per source instead of one per source and metric.

Why it’s built this way

Metrics are SQL queries in configuration, not compiled code
PostgreSQL already exposes its statistics as views, so a query per metric is enough, and DBAs can add or change metrics without touching the Go code.
Reuse existing storage and Grafana instead of building a UI
The project set out to do the minimal work needed; storing numbers and drawing charts were already solved by Postgres, Prometheus and Grafana.
Least privilege on monitored servers
Monitoring should not need superuser on production databases, so the collector runs as a pg_monitor role and creates helper functions only with --create-helpers; managed services get presets that use only what their low-privilege users can see.

Timeline

  1. 2016

    Kaarel Moppel starts the project

  2. 2017

    First release, as pgwatch2

  3. 2024

    pgwatch 3.0.0, rewritten by Pavlo Golub; pgwatch2 is archived

  4. 2025

    pgwatch 4.0 in September

  5. 2026

    pgwatch 6.0: Prometheus exporters as sources, rebuilt dashboards

Sources11 · facts checked 23 October 2026

Improve this page on GitHub