What goes in a PostgreSQL monitoring dashboard in Metabase?
A PostgreSQL monitoring dashboard tracks uptime, replication lag, connections, cache hit rate, latency, and storage for one instance or a whole fleet. Metabase connects to PostgreSQL natively, so you can build it straight from the database's own pg_stat views — or from Prometheus and Datadog exporter rollups if you already collect them.
For: DBAs, platform engineers, and application owners. Grain: one snapshot per instance per interval. Source:
pg_stat_* views, snapshotted — the raw views are cumulative counters.
What does a PostgreSQL monitoring dashboard look like?
Here’s the layout this guide builds. Instance health sits at the top so a glance tells you whether anything needs a human; connections, latency, and cache come next because that’s where load shows up first; replication, storage, and slow queries sit at the bottom for when you’re diagnosing rather than checking.

An example PostgreSQL monitoring dashboard in Metabase, built from snapshotted pg_stat views. Figures are illustrative.
Which cards belong on a PostgreSQL monitoring dashboard?
The eight below cover availability, contention, and capacity — the three ways a Postgres instance usually gets into trouble.
- Server uptime and last restart, per instance (number)
- Replication lag in bytes and seconds, per standby (line)
- Storage used and growth rate, with days-to-full (line + number)
- Connections in use versus
max_connections(gauge + line) - Query latency, mean and p95, by database (line)
- Cache hit rate per database, from
blks_hitandblks_readdeltas (line) - Checkpoints requested versus timed, plus buffers written (bar)
- Top statements by total execution time, from
pg_stat_statements(table)
What data does the dashboard need?
pg_stat_databasefor commits, rollbacks, block hits and reads, deadlocks, and backend counts.pg_stat_replicationon the primary, orpg_last_xact_replay_timestamp()on standbys, for lag.pg_stat_statements(extension) for per-statement calls, total and mean execution time, and rows.pg_stat_bgwriterfor checkpoint behaviour, andpg_database_size()for storage growth.- A snapshot table of your own — the views are cumulative, so the dashboard charts deltas between snapshots, not the raw values.
How do you build it?
- Create a read-only monitoring role and grant it
pg_monitor, so it can see statistics for sessions it does not own. - Schedule a small job that appends the
pg_stat_*readings to a snapshot table every minute or five, tagged with instance and timestamp. - Connect that database to Metabase (see the PostgreSQL data source guide) and model the snapshot deltas once, as a shared model.
- Build one saved question per card against the model, so every card agrees on what a “rate” means.
- Add filters for instance, database, and date range, and set the dashboard to auto-refresh if it goes on a wall display.
Example card SQL
SELECT
s.snapshot_at,
s.database_name,
ROUND(
100.0 * s.blks_hit / NULLIF(s.blks_hit + s.blks_read, 0), 2
) AS cache_hit_pct,
s.numbackends AS active_connections,
s.max_connections,
ROUND(100.0 * s.numbackends / NULLIF(s.max_connections, 0), 1)
AS connection_pct,
s.xact_commit + s.xact_rollback AS transactions,
ROUND(
100.0 * s.xact_rollback / NULLIF(s.xact_commit + s.xact_rollback, 0), 3
) AS rollback_pct,
s.deadlocks,
s.replication_lag_bytes / 1024 / 1024 AS replication_lag_mb
FROM pg_stat_snapshots s
WHERE s.snapshot_at >= now() - interval '7 days'
ORDER BY s.database_name, s.snapshot_at; Related
Metrics
Integrations
Dashboards
FAQ
What is a PostgreSQL monitoring dashboard?
Can Metabase query pg_stat views directly?
pg_stat_* views are ordinary relations, so a native SQL question can read pg_stat_database, pg_stat_user_tables, pg_stat_replication, and (with the extension enabled) pg_stat_statements. Two caveats: the monitoring role needs pg_monitor to see other sessions' query text, and most of these views hold cumulative counters since the last stats reset — so a raw reading is a lifetime total, not a rate.Why are my cache hit and transaction numbers flat?
blks_hit, xact_commit, and friends only ever climb since the last pg_stat_reset(), so a line chart of them looks like a straight ramp and a percentage computed from them is a lifetime average that barely moves. Snapshot the counters on a schedule into your own table and chart the delta between consecutive snapshots — that is what makes a spike visible on the day it happens.What is a good cache hit rate for PostgreSQL?
shared_buffers, or a new query is scanning cold data. Treat it as a directional signal rather than a target to optimize: a warehouse-style database doing large sequential scans will legitimately sit lower, and pairing it with a cache hit rate trend beats staring at a single reading.How do I monitor replication lag?
pg_stat_replication gives you per-standby byte lag via pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn); on a standby, pg_last_xact_replay_timestamp() gives you time lag. Chart both — byte lag catches a standby that has stalled on a burst of writes, time lag catches one that has stopped replaying altogether — and alert on the time lag, since that is what a read-replica user actually experiences.Should I monitor Postgres with Metabase or with a dedicated tool?
How do I find slow queries?
pg_stat_statements extension and rank by total_exec_time rather than mean_exec_time — the query that costs you the most is usually a fast one run ten million times, not the nightly report. Add calls, rows, and cache hit ratio per statement to the table, and remember the view normalizes literals, so each row is a query shape rather than a single execution.