Checks reference
87 checks across 14 areas. Every report lists all of them, marking each passed, flagged, skipped (missing privilege or extension) or not applicable (for example, self-hosted-only checks on a managed provider).
Severity: critical = act now, warning = fix soon, info = worth knowing.
Queries
| Check | Looks for | Flags when |
|---|---|---|
| Query statistics are collected | Whether pg_stat_statements is installed. | Info when it is missing. |
| No single query dominates the workload | The share of total application query time taken by the top statement, after catalog and tooling queries are excluded. | Warning when one statement is 30% or more of at least 60 s of recorded application query time. |
| No query is slow on average | Statements called 5+ times whose mean time is high. | Warning for any averaging 1 second or more. |
| Queries don't spill to disk | Statements writing large temp files because work_mem is exceeded. | Info at roughly 800 MB or more of temp writes per statement. |
| Query history isn't being evicted | Whether pg_stat_statements is dropping old statements. | Info when any statements were evicted. |
Tables
| Check | Looks for | Flags when |
|---|---|---|
| Large tables are read via indexes | Tables over 50,000 rows read mostly by sequential scan. | Warning when seq scans exceed 100 and 5x the index scans. |
| Updates are mostly HOT | The share of updates done as heap-only tuple (HOT) updates on tables with many updates. | Info when a table with 10,000+ updates since the stats reset has under 50% HOT updates. |
| Every table has a primary key | Tables without a primary key. | Info for a table with 1,000+ rows or over 8 MB; smaller ones are only noted. |
| Planner statistics are fresh | Tables with many changes since the last ANALYZE. | Warning when 20%+ of a 10k+ row table changed and it wasn't analyzed in the last day. |
| Tables aren't bloated | Measured wasted space in the largest tables (needs pgstattuple). | Warning at 40%+ bloat when the waste is at least 5% of the database (never under 8 MB, never required above 100 MB); critical at 60%+ when it is 1 GB or a fifth of the database. |
Indexes
| Check | Looks for | Flags when |
|---|---|---|
| No invalid indexes | Indexes left invalid by a failed CREATE INDEX CONCURRENTLY. | Warning for any. |
| No large unused indexes | Indexes over 8 MB that were never scanned since the stats reset. | Info for any. |
| No duplicate indexes | Identical indexes on the same table. | Info when they waste 1 MB or more; warning at 100 MB. Smaller ones are only noted. |
| No redundant (prefix) indexes | Non-unique indexes that are the leading columns of a longer index. | Info when the redundant index is 1 MB or more; smaller ones are only noted. |
| Foreign keys are indexed | Foreign-key columns with no supporting index. | Warning when a referencing table has 10k+ rows; info at 1,000+. Smaller tables are only noted. |
Schema & integrity
| Check | Looks for | Flags when |
|---|---|---|
| Sequences are far from their maximum | How much of each sequence's range is used (integer keys run out at about 2.1 billion). | Warning at 70% used; critical at 90%. |
| Constraints are validated | Constraints added NOT VALID that were never validated. | Info for any. |
| No triggers are disabled | Triggers switched off. | Info for any. |
Blocked transactions
| Check | Looks for | Flags when |
|---|---|---|
| Nothing is blocked on locks | Queries currently waiting for a lock held by another session. | Warning; critical if anything waits 30 s or more. |
| No sessions idle in a transaction | Sessions that opened a transaction and went quiet. | Warning at 5 minutes idle. |
| No very long-running queries | Statements that have been executing for a long time. | Info at 5 minutes; warning at 30 minutes. |
| No stuck prepared transactions | Two-phase-commit transactions left prepared. | Warning after 5 minutes; critical after 1 hour. |
| Nothing is holding back vacuum | The oldest xmin held by a session, slot, prepared transaction or standby. | Warning at 10 million transactions old; critical at 100 million. |
Connections
| Check | Looks for | Flags when |
|---|---|---|
| Connections are below the limit | Client connections as a share of max_connections. | Warning at 80%; critical at 90%. |
| Idle connections are under control | Many mostly-idle connections. | Info at 50+ idle and 40%+ of max_connections. |
| Remote clients use TLS | Remote sessions connected without encryption (excludes this scan and provider services). | Warning for any. |
| The server enforces TLS | Whether this scan could only connect without TLS. | Warning when a remote server accepted an unencrypted connection. |
| No connections are stale | Sessions idle for more than 30 minutes, and the keepalive and timeout settings that would end a dead one. | Info at 5 or more; warning at 20 or more, or when they are hours old with keepalives at the OS default. |
Replication & WAL
| Check | Looks for | Flags when |
|---|---|---|
| Replication slots are healthy | Inactive or lost replication slots that pin WAL. | Warning; critical if a slot is lost or retains 5 GB+. |
| Standbys keep up | Replay lag of connected replicas. | Warning at 60 s or 1 GB behind; critical at 300 s or 10 GB. |
| Replication slots are below the limit | Slots in use versus max_replication_slots. | Warning at 80%. |
| Checkpoints are timed, not forced | Share of checkpoints forced by WAL volume. | Warning when over half of 50+ checkpoints are forced. |
| WAL on disk is bounded | The size of pg_wal versus max_wal_size. | Warning when pg_wal is over twice max_wal_size and 1 GB. |
| WAL archiving is working | The most recent archive attempt. | Critical when the latest attempt failed. |
| This replica is receiving WAL from its primary | On a standby: whether the WAL receiver is running and streaming, and how long since it heard from the primary. | Critical when a streaming standby has no active receiver; warning when it hasn't heard from the primary in 2 minutes. |
| Replay is running on this replica | Whether replay of received WAL is paused on a standby. | Warning when paused. |
| This replica applies WAL as fast as it receives it | WAL received but not yet applied on a standby. | Warning at 256 MB; critical at 2 GB. |
| Queries on this replica aren't being cancelled | Recovery conflicts recorded on a standby. | Warning at 5 or more cancelled queries. |
| The synchronous standby is connected | synchronous_standby_names against the standbys actually connected. | Critical when synchronous replication is configured and no synchronous standby is connected. |
| Connected standbys are streaming | The state of each standby in pg_stat_replication. | Warning for a standby that is not streaming and is more than 16 MB behind, or is starting or in backup. |
Backups & recovery
| Check | Looks for | Flags when |
|---|---|---|
| WAL archiving is on (point-in-time recovery is possible) (self-hosted) | archive_mode and wal_level on self-managed servers. | Warning at 1 GB+ (info below); off means you can only restore to the last base backup, never a point in time. |
| The archive command really archives (self-hosted) | An archive_command that is a no-op such as /bin/true. | Critical when found. |
| WAL is being archived promptly (self-hosted) | How many WAL segments are waiting to be archived. | Warning at 10 segments behind; critical at 100. |
| Data loss window is bounded (archive_timeout) (self-hosted) | archive_timeout when archiving is on. | Info when 0 on a self-managed server. |
| Replicas aren't your only protection (self-hosted) | Standbys present while WAL archiving is off. | Warning when replicas exist but nothing is archived. |
| Nothing important sits outside physical backups | Unlogged tables and custom tablespaces. | Info for any. |
| Provider backups are configured (supabase, neon) | Reminds you what your provider covers (this can't be verified from SQL). | Never flags; shows what to confirm in your provider's dashboard. |
Vacuum & bloat
| Check | Looks for | Flags when |
|---|---|---|
| Dead rows are under control | Tables with a large share of dead rows. | Warning at 20%+ (and 10k+ rows); critical at 50%+. |
| Autovacuum is on | The autovacuum setting. | Critical when off. |
| Busy tables get vacuumed | Tables with lots of writes that were never vacuumed. | Warning for any table with 10k+ writes and no vacuum. |
| Tables aren't near a forced freeze | Table XID age versus autovacuum_freeze_max_age. | Warning at 80%; critical at 95%. |
| Transaction ID age is below the limit | The oldest transaction ID versus the ~2.1 billion limit. | Warning at 1.0 billion; critical at 1.5 billion. |
| MultiXact ID age is below the limit | The oldest MultiXact ID versus its limit. | Warning at 1.0 billion; critical at 1.5 billion. |
Configuration
| Check | Looks for | Flags when |
|---|---|---|
| fsync is on | The fsync setting. | Critical when off, unless the provider owns durability (Neon). |
| full_page_writes is on | The full_page_writes setting. | Critical when off, unless the provider owns durability (Neon). |
| Statistics collection is on | The track_counts setting. | Critical when off. |
| Commits are durable | The synchronous_commit setting. | Info when off. |
| Data checksums are enabled (self-hosted) | Whether page checksums are on. | Info when off. |
| shared_buffers is tuned (self-hosted) | Whether shared_buffers is still the 128 MB default on a larger database. | Info when default and the database is over 2 GB. |
| work_mem suits the workload | Default work_mem alongside heavy temp-file use. | Info when work_mem is 4 MB and over 1 GB was spilled. |
| PostgreSQL version is supported | The major version's end-of-life date. | Critical after end of life; warning within 180 days. |
Capacity & scaling
| Check | Looks for | Flags when |
|---|---|---|
| The working data fits in memory | Database size against shared_buffers / effective_cache_size, and the share of reads served from cache. | Warning when data is over twice the memory and the cache hit rate is under 97%; info under 99%. |
| Sessions aren't waiting on disk | The share of currently active sessions waiting on I/O, from a wait-event snapshot. | Warning when 40% or more of at least 3 active sessions wait on I/O. |
| Active sessions aren't all on CPU | Active sessions that are running (no wait event) against max_connections, from a snapshot. | Info when at least 8 sessions, and a quarter of max_connections, are running on CPU together. |
| Queries aren't spilling to temp files heavily | Temp file volume per hour since the last statistics reset. | Info at 1 GB per hour or more; warning at 5 GB. |
| Reads and writes are balanced, or there is a replica | Read-to-write row ratio, when the database is also showing memory, disk or CPU pressure and has no replica. | Info when reads are 5 times writes (or more), pressure signals are present, and no standby is attached. |
| No single table dominates the database | The largest table's size and its share of the database. | Info at 100 GB, or 20 GB when it is 60% or more of the database. |
| Autovacuum workers have spare capacity | Autovacuum workers running now against autovacuum_max_workers. | Info when every worker is busy at scan time. |
| Footprint on disk is known | This database, the other databases on the server and WAL, added up. Postgres cannot see the volume's size or free space. | Never flags on its own. Warning when WAL alone is over half the size of the data, which usually means retention is stuck. The total is for you to compare with your volume. |
Logs & activity
| Check | Looks for | Flags when |
|---|---|---|
| Lock waits are logged | The log_lock_waits setting. | Info when off. |
| Slow queries are logged | The log_min_duration_statement setting. | Info when -1 (off). |
| I/O timing is recorded | The track_io_timing setting. | Info when off. |
| No deadlocks | Deadlocks since the stats reset. | Info for any; warning at 10+. |
| Rollback rate is normal | Rollbacks as a share of transactions. | Info at 20%+ (with 10k+ transactions). |
| No data-page checksum failures | Checksum failures recorded by the database. | Critical for any. |
Security
| Check | Looks for | Flags when |
|---|---|---|
| Few privileged login roles | Login roles with SUPERUSER, BYPASSRLS or REPLICATION (provider-managed roles ignored). | Info when there is more than one. |
| RLS policies restrict rows | Policies whose gating expression is always true, split by whether they allow writes or only reads. | Warning when a write (INSERT, UPDATE, DELETE, ALL) policy is always true. Info when only SELECT policies are, since public read access is normal for reference data. |
| SECURITY DEFINER functions pin search_path | SECURITY DEFINER functions without a fixed search_path. | Warning for any. |
| Public tables are protected by RLS (supabase) | Public-schema tables the anon role can read with row-level security off. | Critical for any (Supabase). |
| No passwordless remote access (self-hosted) | pg_hba.conf rules using 'trust' for non-local addresses (needs access to the hba view). | Critical for any. |
| Passwords are hashed with SCRAM | The password_encryption setting. | Warning when md5. |
| TLS is enabled on the server (self-hosted) | The ssl setting. | Warning when off. |
| Public schema is locked down | Whether every role can create objects in the public schema (PostgreSQL 14 and earlier). | Warning when PUBLIC has CREATE. |
Extensions
| Check | Looks for | Flags when |
|---|---|---|
| Extensions are up to date | Installed extensions with a newer version available. | Info for any. |
| pg_net's response table is kept small | The size and dead-row share of net._http_response, and how many requests are waiting in net.http_request_queue. | Warning when the response table is over 100 MB, or over 10 MB with more dead than live rows; critical at 1 GB. Warning when 1,000+ requests are queued. |
| pg_cron run history is kept small | The size of cron.job_run_details, where pg_cron logs every run. | Info at 100 MB; warning at 1 GB. |
| pg_cron jobs are succeeding | Scheduled jobs with failed runs in the last 7 days. | Warning when any job failed. Needs the job owner or a superuser, because pg_cron hides other roles' jobs. |
No overall score
A database isn't graded. Findings are ranked by severity (critical, warning, info) so the most urgent one is first.
Known limitations
- Statistics-based checks reflect the time since the last stats reset (or restart on some providers).
- Bloat is measured only when
pgstattupleis installed; otherwise the dead-tuple check covers the common cases. - Sequence checks need
SELECTon the sequences; unreadable ones are reported, not guessed. - Host-level signals (CPU, memory, disk, OOM kills) and server log files aren't visible over SQL.