C01 — Connection & Availability¶
Whether the server can be reached and used at all — TCP/SSL reachability, whether the server's own readiness signals (pg_isready-style checks done from inside a session) look healthy, and whether active connections are approaching max_connections. This is the category worth checking first: a problem here can make every other category's data stale or unreachable.
6 checks, PGHF01-001 through PGHF01-006. Every check here is PGHF-namespace and built-in — see Metadata Columns Explained for what each field below actually means, and Resetting to Defaults for why these definitions can't be hand-edited in place.
PGHF01-001 — SSL/TLS enabled¶
Without SSL, credentials and every row of data travel the network in plaintext — trivially interceptable on any shared or untrusted network path between client and server.
How to fix
Enable SSL and stop accepting plaintext connections on any non-loopback path:
- Provide a certificate/key pair (a CA-signed one for production,
openssl req -x509 -newfor a quick test) and pointssl_cert_file/ssl_key_fileat them. - Turn it on and restart (
sslisPGC_SIGHUP-only from the client-negotiation side but the listener itself needs a restart the first time it's enabled):
- In
pg_hba.conf, change the relevanthostlines tohostssl(or drop plainhostentries entirely) so a client can't fall back to an unencrypted connection, then reload:
PGHF01-002 — PostgreSQL version¶
The running major version is the baseline every EOL/support-window and feature-availability judgment is made against (see pghf.pg_eol_dates) — a version past end-of-life receives no further security fixes from the PostgreSQL project.
The check's recorded value is the number of whole years the running major version is past its EOL date in pghf.pg_eol_dates (lower is better): negative means still supported, 0 means past EOL by under a year, 1 means one to two years past, and so on. The default threshold (info > -1, warning > 0, critical > 1) therefore never needs a hardcoded version number or date — keeping pghf.pg_eol_dates refreshed from the PostgreSQL versioning policy is the only maintenance. A major version newer than the reference data is assumed supported and reported as -99; the actual major version, full version string, and EOL date are kept in the result's detail.
How to fix
There's no in-place fix — this is a planning signal, not a config change:
- Check this server's major version against
pghf.pg_eol_dates(or the PostgreSQL versioning policy) to see how much runway is actually left. - Plan a major-version upgrade well before EOL —
pg_upgradefor a maintenance-window upgrade, or logical replication into a new-version instance for a near-zero-downtime cutover. - Before upgrading, run every
PGHF10-*(pg_upgrade readiness) check in this framework and resolve what it flags.
PGHF01-003 — Connection saturation¶
Once active connections reach max_connections, every new connection attempt is refused outright — including the application's own retries and an operator's own psql session trying to diagnose the problem.
How to fix
Raising max_connections is usually the wrong first move — each backend costs real memory whether idle or busy, and PostgreSQL's own connection handling degrades well before the limit if it's pushed too high:
- Put a connection pooler (PgBouncer, PgCat, or the application's own pool) in front of the database in transaction-pooling mode, so hundreds of app-level connections share a much smaller number of real backends.
- If genuine concurrent-backend demand justifies it, raise the limit deliberately and re-check available RAM (
work_mem-heavy queries multiply per backend):
max_connections requires a full server restart to take effect, not just a reload.
PGHF01-004 — Oldest idle-in-transaction session age¶
A long-lived idle-in-transaction session holds its snapshot open, blocking autovacuum from reclaiming dead tuples and holding any locks it acquired — a single forgotten transaction can quietly stall vacuum and lock cleanup cluster-wide.
How to fix
For the immediate problem, find and end the offending session:
SELECT pid, usename, state, now() - xact_start AS age, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY age DESC;
SELECT pg_terminate_backend(<pid>);
To prevent recurrence, set a ceiling so no session can hold a transaction open indefinitely again:
Then check the application for a code path that opens a transaction without a COMMIT/ROLLBACK on every exit path, including error handling.
PGHF01-005 — Per-database connection counts¶
A single noisy database can quietly monopolize most of the cluster's connection budget, starving every other database sharing the same instance — visible here well before any one of them hits PGHF01-003's cluster-wide saturation alert.
How to fix
Identify the outsized database from the observed breakdown, then either fix its connection usage at the source (pooling — see PGHF01-003) or cap it explicitly so it can't starve its neighbors:
Existing connections aren't affected — this only blocks new ones once the limit is hit.
PGHF01-006 — pg_hba TRUST on non-loopback¶
A trust rule on a non-loopback address grants passwordless access to anyone who can reach that network path — replace it with scram-sha-256 immediately; this is one of the few findings in the whole framework worth treating as an active incident, not a backlog item.
How to fix
Treat this as urgent, not routine maintenance:
- In
pg_hba.conf, change everytrustentry on a non-loopback address (anything other than127.0.0.1/32/::1/128, or an unmappedlocalsocket) toscram-sha-256. - Make sure every affected role actually has a password set first (
ALTER ROLE ... PASSWORD ...), or those connections will simply start failing. - Reload:
- Confirm the change took effect against
pg_hba_file_rulesbefore closing this out — atrustrule that's merely commented out or shadowed by an earlier matching line won't show here as fixed.
Continue to C02 — pgBackRest Configuration & WAL Archiving.