Skip to content

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:

  1. 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.
  2. 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.
  3. 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

  1. Configure enough huge pages at the OS level first (Linux): sysctl -w vm.nr_hugepages=<count> (persist in /etc/sysctl.conf), sized against shared_buffers.
  2. Then have PostgreSQL use them:
ALTER SYSTEM SET huge_pages = 'try';  -- or 'on' once OS-level pages are confirmed available

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

ALTER SYSTEM SET hot_standby_feedback = on;  -- on the standby
SELECT pg_reload_conf();

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:

  1. Check the PostgreSQL log around the restart time for a PANIC/FATAL message.
  2. Check the OS/kernel log (dmesg, journalctl -k) for an OOM kill or hardware event around the same time.
  3. If it was an OOM kill, check PGHF03-002's work_mem worst-case-total sizing and shared_buffers against 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

  1. 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.
  2. If storage is the bottleneck, raise max_wal_size on the standby so restartpoints are triggered on a more forgiving schedule rather than constantly racing incoming WAL:
ALTER SYSTEM SET max_wal_size = '4GB';  -- run on the standby
SELECT pg_reload_conf();
  1. 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.