C13 — OS & Resource-Level Checks¶
The subset of "OS-level" health that's actually visible from inside a database session — checkpoint and I/O timing, buffer-pool eviction pressure by context, and similar catalog-visible resource signals. A PL/pgSQL function running inside PostgreSQL structurally cannot read the host's own kernel settings or filesystem directly; see Third-Party Notices for exactly which OS-level checks that ruled out, and what external tooling to use for those instead.
10 checks, PGHF13-001 through PGHF13-010. 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.
PGHF13-001 — Checkpoint sync time¶
A high average sync time per checkpoint means the underlying storage can't flush dirty pages fast enough — an I/O bottleneck that shows up here as checkpoint latency well before it's obvious anywhere else.
How to fix
This is a storage-layer signal, not a PostgreSQL setting problem:
- Spread the write load rather than concentrating it — raise
checkpoint_completion_target(PGHF03-007) so writes happen across the full interval instead of a burst. - Check the underlying storage's actual write throughput/latency directly (
iostat, cloud-provider disk metrics) — if it's saturated, this needs faster storage or a smaller working set, not a PostgreSQL config change. - Confirm
full_page_writes/wal_compression(PGHF03-009) aren't needlessly inflating what gets written per checkpoint.
PGHF13-002 — pg_stat_io evictions¶
A high eviction count directly means the active working set doesn't fit in shared_buffers — the same underlying problem as PGHF03-001/PGHF03-016's cache-hit signals, seen from the buffer-pool-management side instead.
How to fix
See PGHF03-001's remediation — raise shared_buffers if the working set genuinely doesn't fit; pg_stat_io's evictions column (broken down by backend type/context) also pinpoints whether it's ordinary backends or a specific process (autovacuum, a bulk load) driving the eviction pressure.
PGHF13-003 — maxwritten_clean¶
A nonzero maxwritten_clean means the background writer is stopping its cleaning pass mid-scan because it hit its per-round write limit — it can't keep pace with the rate dirty pages are being generated.
How to fix
ALTER SYSTEM SET bgwriter_lru_maxpages = 1000; -- raise the per-round write ceiling
ALTER SYSTEM SET bgwriter_delay = '100ms'; -- optionally run rounds more often
SELECT pg_reload_conf();
If this is persistently high even after raising the limits, the real bottleneck is likely shared_buffers sizing (PGHF03-001) or underlying storage throughput, not the background writer's own settings.
PGHF13-004 — huge_pages setting¶
Huge pages reduce TLB pressure for a large shared_buffers allocation — on a server with a sizeable buffer pool, leaving this off (or unavailable) leaves measurable memory-access performance on the table.
How to fix
- Configure enough huge pages at the OS level first (Linux):
sysctl -w vm.nr_hugepages=<count>(persist in/etc/sysctl.conf), sized againstshared_buffers. - Then have PostgreSQL use them:
Requires a restart. huge_pages = try (the default) silently falls back to normal pages if the OS doesn't have enough configured — check postgres -C huge_pages or the log after restart to confirm it actually took effect, not just that the setting was accepted.
PGHF13-005 — Temp file spill¶
Excessive temp file volume degrades query performance and adds wear to the underlying storage — the same fundamental signal as PGHF03-015, tracked here alongside the rest of this category's host-resource view.
How to fix
See PGHF03-015's remediation — find the specific query with log_temp_files, then raise work_mem (cluster-wide or scoped) to fit the operation in memory instead of spilling.
PGHF13-006 — Query conflicts¶
A query conflict on a standby means autovacuum or primary activity forced PostgreSQL to cancel a running standby query — frequent conflicts point at hot_standby_feedback or max_standby_streaming_delay tuning that needs revisiting.
How to fix
If feedback is already on and conflicts are still frequent, raise max_standby_streaming_delay on the standby to give it more slack before PostgreSQL forces a cancellation — this trades a bit more replication lag tolerance for fewer canceled standby queries.
PGHF13-007 — max_connections advisory¶
Past roughly 500, max_connections itself starts degrading lock-manager performance and wastes 5-10MB of RAM per idle slot even when unused — a genuinely oversized setting is a cost independent of how close to saturation the server actually is (that's PGHF01-003's job).
How to fix
See PGHF01-003's remediation — a connection pooler (PgBouncer/PgCat) almost always addresses the actual need better than a very high max_connections. If a genuinely high connection count is required, confirm it against real hardware capacity rather than leaving it at a value chosen without that check.
PGHF13-008 — Postmaster uptime¶
A postmaster that restarted recently and unexpectedly is worth explaining — an OOM kill, a kernel panic, or a PostgreSQL-level PANIC all leave this as the first visible symptom, well before anyone thinks to check the logs.
How to fix
No config fix — this is a signal to investigate, not a setting to change:
- Check the PostgreSQL log around the restart time for a
PANIC/FATALmessage. - Check the OS/kernel log (
dmesg,journalctl -k) for an OOM kill or hardware event around the same time. - If it was an OOM kill, check
PGHF03-002'swork_memworst-case-total sizing andshared_buffersagainst actual available RAM — an OOM restart is often the first hard evidence that memory settings are oversized for the box.
PGHF13-009 — Standby restartpoint completion¶
Requires PostgreSQL 17+; Standby only.
Restartpoints are the standby-side equivalent of checkpoints. PGHF03-008/PGHF13-001 are explicitly primary-only — a standby whose restartpoints are falling behind is accumulating recovery-time risk (a crash/promotion has to replay further back) with zero visibility today, despite the data existing in a view this project already queries for the primary-side metric.
How to fix
- Check whether the standby is under sustained I/O or CPU pressure that's preventing restartpoints from completing in time — the same investigation as a primary-side forced-checkpoint problem (
PGHF03-008), just on the recovery side. - If storage is the bottleneck, raise
max_wal_sizeon the standby so restartpoints are triggered on a more forgiving schedule rather than constantly racing incoming WAL:
- Persistent incompletion is a real crash-recovery-time risk — treat it with the same urgency as primary-side checkpoint pressure, not as a cosmetic standby metric.
PGHF13-010 — Async I/O method¶
Requires PostgreSQL 18+.
Knowing which I/O method is actually active is the same "silent config drift" class this project's huge_pages/wal_compression/random_page_cost checks already exist for — a server left on a suboptimal io_method for its platform loses throughput with no error raised anywhere.
How to fix
No universal fix — the best io_method depends on the platform and kernel:
ALTER SYSTEM SET io_method = 'io_uring'; -- where supported (Linux with io_uring); check pg_settings.enumvals for what this build actually offers
SELECT pg_reload_conf();
worker is the safe, portable default; io_uring (Linux-only, and only where compiled in) can improve throughput for I/O-heavy workloads but needs testing on the actual target platform before switching production traffic to it.
Continue to C14 — WAL Growth & Generation Rate.