Skip to main content
Metrics type: Key MetricsCategory: Executive Overview

At a glance

A single 0 to 100 composite that answers “is my PostgreSQL instance healthy right now?” without making a platform lead read five dashboards. It folds the five things that actually take a production database down: query latency, query errors, replication health, autovacuum currency, and connection-pool headroom. Each factor scores 0 to 1; the score is their product, scaled to 100. Because it is multiplicative, any single factor collapsing drags the whole score down hard, which is the point: one broken pillar (a stalled standby, a wraparound-risk autovacuum) is an emergency even if everything else is green. Read it as a traffic light: green is steady-state, amber means one pillar is wobbling, red means act now.

Calculation

The score is the product of five independent health fractions, each in the range 0 to 1, scaled to 100:
Each factor is derived from its own source and clamped to 0 to 1:
  • p95 latency factor reads p95 query execution time from pg_stat_statements. At or below the healthy threshold it scores 1.0; it decays towards 0 as latency climbs past the alert level (200ms by default).
  • error-free factor reads the rolling query error rate from pg_stat_database (rollbacks and failed statements over total). Zero errors scores 1.0; it decays as the rate rises past the 1% alert level.
  • replication factor reads pg_stat_replication on the primary. A reachable standby with sub-second lag scores 1.0; lag growth decays it, and an unreachable / BROKEN standby collapses it towards 0.
  • autovacuum factor reads the maximum time since last (auto)vacuum across user tables. Recently vacuumed scores 1.0; it decays as the oldest table ages past 24 hours, reflecting accumulating bloat and transaction-ID wraparound risk.
  • pool-headroom factor reads active plus idle-in-transaction backends against max_connections. Comfortable headroom scores 1.0; it decays towards 0 as saturation approaches 100%.
The multiplicative form is deliberate. An average would let four healthy pillars mask one failing one (4 x 1.0 + 1 x 0.2, averaged, is still 84, which would hide a stalled standby). The product makes the score honest: 1.0 x 1.0 x 1.0 x 1.0 x 0.2 = 0.20, a score of 20, which correctly screams that something is broken.

Worked example

A platform team runs a primary PostgreSQL 16 instance with one streaming standby behind PgBouncer, serving an internal analytics and orders API. Two snapshots, taken on 22 Apr 26. Snapshot A, 09:00 BST, steady state: Composite: 1.00 x 0.99 x 1.00 x 1.00 x 1.00 = 0.99 -> score 99. The gauge is solid green. Nothing to do. Snapshot B, 14:20 BST, same day, after a batch import: Composite: 0.82 x 0.99 x 0.55 x 0.78 x 0.92 = 0.32 -> score 32. The gauge is red and the <70 alert has fired. The story the score tells: a large batch import kicked off at 13:30. It generated heavy write WAL, which the standby is struggling to apply (lag jumped to 14s, the single biggest drag on the score at 0.55). The same write load is generating dead tuples faster than autovacuum is keeping up (oldest table now 27h, factor 0.78), and the extra query volume has pushed p95 latency just over threshold (0.82). No single factor is catastrophic on its own, but the multiplicative composite correctly compounds three simultaneous wobbles into a red 32. The platform team’s read, in order of leverage:
  1. Replication is the dominant drag (0.55). A 14-second standby lag means failover would lose up to 14 seconds of writes and that read replicas are serving stale data. Open Replication Lag (seconds) and confirm whether the standby is apply-bound (catching up slowly) or network-bound.
  2. Autovacuum is falling behind (0.78). The batch created bloat faster than autovacuum reclaimed it. Check Top Tables by Dead Tuples and consider a manual VACUUM on the hot table or tuning autovacuum_vacuum_cost_limit.
  3. Latency over threshold is a symptom, not a cause (0.82). It will self-correct once the batch finishes and the standby catches up. Do not chase it independently.
Three things worth remembering:
  1. A low score names the broken pillar; the drill-down tells you which one. Never act on the composite alone. Open the factor breakdown, find the factor nearest 0, and start there. The lowest factor is your highest-leverage fix.
  2. The score is multiplicative on purpose. One collapsed pillar should cap the whole score, because in production one collapsed pillar (a dead standby, a wraparound-risk table) is an emergency regardless of how healthy everything else is.
  3. Two cards live outside the composite. Disk usage and backup currency are deliberately excluded so they cannot be masked. A 99 health score with a disk at 94% full is still an imminent outage; always read this hero card next to Database Disk Usage % and the backup card.

Sibling cards to reference together

Reconciling against the source

Where to look in PostgreSQL’s own tooling:
There is no single native command that produces this score, it is a Vortex IQ composite. To reconcile it, verify each factor against its own source: p95 latency: SELECT query, mean_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC; (requires the pg_stat_statements extension). error rate: SELECT datname, xact_commit, xact_rollback FROM pg_stat_database; for the rollback ratio. replication: SELECT client_addr, state, replay_lag FROM pg_stat_replication; on the primary. autovacuum age: SELECT relname, last_autovacuum, n_dead_tup FROM pg_stat_user_tables ORDER BY last_autovacuum NULLS FIRST; pool headroom: SELECT count(*) FROM pg_stat_activity; against SHOW max_connections; Managed-service console: on Amazon RDS / Aurora, Performance Insights plus the CloudWatch metrics for ReadLatency, ReplicaLag, DatabaseConnections and MaximumUsedTransactionIDs. On Cloud SQL, the system insights and the replication/replica_lag, database/postgresql/num_backends, and transaction-ID metrics. Each maps to one factor.
Why our number may legitimately differ from a manual factor-by-factor reconstruction: Cross-connector reconciliation:

Known limitations / FAQs

Why is my score 32 when four of the five factors are perfectly healthy? Because the score multiplies the factors rather than averaging them. One factor at 0.55 (a 14-second standby lag, say) caps the whole composite near that level no matter how green the other four are: 1.0 x 1.0 x 1.0 x 1.0 x 0.55 is still only 0.55. This is intentional. In production, one broken pillar is an emergency, and an averaging score would let four healthy pillars hide it. Open the factor breakdown, find the factor nearest 0, and fix that. Does the score include disk usage and backups? No, deliberately. Disk usage and backup currency are tracked as their own hero cards and kept out of the composite so they can never be masked by a healthy score. A database can read 98 here while sitting at 95% disk or with a 4-day-old backup. Always read this card alongside Database Disk Usage % and Last Successful Backup (hours ago). My score is amber (around 75) but everything feels fine. Should I worry? Amber usually means one factor is sitting in its decay zone without having fully collapsed: a standby a few seconds behind, autovacuum a little late, latency just over threshold. It is not an emergency, but it is the moment to look, because the multiplicative form means a second wobble while you are already amber will push you red fast. Treat amber as “investigate before the next traffic peak”, not “ignore”. The score has been slowly declining over the 7-day trend with no obvious incident. What causes that? A slow decline almost always means creeping autovacuum starvation or a standby gradually falling behind under steady write growth. These are exactly the slow-bleed problems the 7-day window is there to catch. Open Oldest Autovacuum Age (hours) and Replication Lag (seconds) and look for a matching upward creep. I do not run a standby. Does the replication factor break the score? No. On a single-instance deployment with no configured standby, the replication factor is set to neutral (1.0) so it neither helps nor harms the composite, since there is nothing to be unhealthy. The factor only becomes active once pg_stat_replication reports a configured standby. Does pg_stat_statements need to be installed for the score to work? The latency factor is most accurate with pg_stat_statements enabled, since that is where per-statement execution times live. Without it, the factor falls back to a coarser latency signal and the score is slightly less precise. The other four factors do not depend on it. Installing the extension (it ships with PostgreSQL and is enabled via shared_preload_libraries) is strongly recommended for an accurate score. Can I change the weighting or the alert threshold? The <70 alert threshold is configurable per profile in the Alert Rules tab. The per-factor decay curves follow the same thresholds you set on the underlying cards (p95, saturation, lag, error rate, autovacuum age), so tuning those cards retunes the composite consistently. There is no separate per-factor weight to set: the factors are equal multiplicands by design, which is what makes a single collapse honest.

Tracked live in Vortex IQ Nerve Centre

PostgreSQL Health Score is one of hundreds of KPI pulses Vortex IQ tracks across PostgreSQL and 70+ other ecommerce connectors. Nerve Centre runs the detection layer; Vortex Mind investigates the cause when something moves; Ask Viq lets you interrogate any number in plain English. Start for free or book a demo to see this metric running on your own data.