shared_buffers

Sets the shared memory pool for caching data pages. Too small: frequent disk reads. Too large: OOM or less OS cache for reads.

HIGH RISK
Safe first move

25% of total RAM; measure pg_buffercache hit ratio; stop when plateau reached

Main risk

Too high → OOM kill or double-buffering (less OS cache); too low → every read misses cache → 14× slower analytics (2094ms vs 145ms, lab-captured).

Tier 2 — Production Guide — Operational guidance is available; evidence scope is stated where relevant.

What We Observed

box

(PG 17.10, 128MB shared_buffers)

table

sb_test 500k rows; sb_wl 100k rows

cold cache

setting

128MB (default)

method

DISCARD ALL (session cache cleared)

plan

Index Scan on sb_test

exec time ms

32.523

source file

warm cache

setting

128MB (same, but pg_prewarm loaded table into cache)

method

pg_prewarm('sb_test') then EXPLAIN ANALYZE

plan

Index Scan on sb_test (all from shared_buffers)

exec time ms

19.198

source file

analytics cold

exec time ms

2094.268

buffers

shared hit=1 read=7160

source file

analytics warm

exec time ms

145.267

buffers

shared hit=7161 read=0

speedup

14.4x vs cold

source file

pg buffercache

total

128MB

used by relations

46-49MB

pct used

36-38%

source file

curve note

Cache hit ratio improves with more buffers until working set fits. Returns flatten once full dataset is resident. Cold→warm speedup = 14.4× on 500k-row analytics sort.

What Was Expected

framing

Two distinct dangers: (1) startup failure if shared_buffers exceeds kernel/container memory limits; (2) performance regression from double-buffering when shared_buffers takes too much RAM away from OS page cache.

danger 1 startup
setting

2GB on 512MB container

container

cl_sb_danger (--memory=512m --memory-swap=512m)

result

Running=true ExitCode=0 OOMKilled=false — modern PG (9.3+) uses mmap; startup does not FATAL, but memory pressure causes OOM under load

historical note

In PG < 9.3 (System V shmem), startup FAILs immediately. PG 9.3+ with mmap: startup succeeds; OOM happens at first heavy access.

source file

danger 2 performance
setting

Too high (>50% RAM) → OS page cache evicted → double-buffering: same data in shared_buffers AND kernel page cache

result

Diminishing returns; additional RAM gives no speedup; checkpoint flush time increases

evidence

cold (2094ms) vs warm (145ms) analytics benchmark shows how critical cache residency is

source file

safe range

15-30% of total RAM as starting point; measure hit ratio with pg_buffercache

source file

Common Pitfalls

  • Believing 'the default changed from 8MB to 128MB across versions'. The EFFECTIVE default is 128MB on all of 14-18 (initdb auto-tunes it); only the compiled boot_val fallback differs (PG14 8MB). Don't quote boot_val as the default.
  • Setting shared_buffers near 100% of RAM. It starves the OS page cache (PostgreSQL's L2) and the work_mem/connection budget; oversizing either blocks startup (could-not-map FATAL) or causes a latent OOM under load.
  • Expecting big shared_buffers to speed up large sequential scans. It won't until shared_buffers exceeds ~4x the table — large seqscans use a 256kB ring buffer (BAS_BULKREAD) by design and rely on the OS cache.
  • Forgetting it needs a RESTART. ALTER SYSTEM + pg_reload_conf leaves pending_restart=true; the new value only takes effect after a server bounce — plan the outage.
  • Sizing far past the hot working set. The measured curve is flat (even slightly worse) once the working set fits; extra shared_buffers is wasted RAM that the OS cache could have used.
  • Treating shared_buffers as 'how much data fits in RAM'. The OS cache already holds most warm data; shared_buffers is about hit-ratio, write coalescing, and latency consistency, not total caching.
  • Changing it without a tested revert value. A too-high value can prevent the server from restarting at all, locking you out until you edit the config back down.

Tuning Guidance

Unlock Incident Pro

The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.

  • Tuning Guidance
Unlock Incident Pro

shared_buffers is the size of PostgreSQL's own shared-memory page cache: the pool of 8kB buffers in which it keeps table and index pages it has read or written. Every backend reads and writes through this pool. It is allocated ONCE at server start (context=postmaster) and cannot change without a restart. Crucially it is NOT PostgreSQL's only cache: the operating system's page cache sits underneath it, so a page evicted from shared_buffers is usually still in OS RAM and re-read cheaply. That two-tier reality is why shared_buffers tuning is about hit-ratio and write coalescing, not 'put all my data in RAM' — and why setting it to a huge fraction of RAM backfires by starving the OS cache.

Measured literally from pg_settings on PG 14/15/16/17/18: setting=16384, unit=8kB => effective default 128MB on ALL FIVE versions (reset_val=16384 everywhere). vartype=integer, min_val=16 (8kB pages => 128kB floor), max_val=1073741823. context='postmaster' => requires a full server RESTART to change; a wrong value blocks startup. HONESTY NUANCE captured by the matrix: boot_val (compiled-in fallback) is 1024 (8MB) on PG14 but 16384 (128MB) on PG15-18 — so PG15 raised the COMPILED fallback, NOT the effective default. A normally created cluster shows 128MB on every version because initdb probes the box and writes a tuned shared_buffers into postgresql.conf at cluster creation. Therefore the correct statement is 'effective default 128MB across 14-18'; the often-repeated 'default changed from 8MB to 128MB' confuses boot_val with the real default.

References

  • PostgreSQL 18 docs — Resource Consumption / Memory: shared_buffers, effective_cache_size, huge_pages (postgresql.org/docs/18/runtime-config-resource.html)
  • PostgreSQL source — buffer ring / BAS_BULKREAD strategy for large scans (src/backend/storage/buffer/freelist.c; GetAccessStrategy / 256kB ring) — observed via pg_buffercache
  • PostgreSQL 18 docs — pg_buffercache and pg_prewarm extensions (postgresql.org/docs/18/pgbuffercache.html, /pgprewarm.html)
  • PostgreSQL 18 docs — pg_settings (boot_val vs reset_val semantics) (postgresql.org/docs/18/view-pg-settings.html)
  • Lab ground truth: truth/pg_settings_pg14.18.tsv (this repo)
  • Lab captures: sb_curve.txt, sb_hitratio.txt, sb_danger.txt} (this repo)

Technical Reference

Category
Resource Usage / Memory
Type
integer (8kB)
Blast radius
cluster
Severity
High
Context
postmaster
Versions
14, 15, 16, 17, 18
Reviewed
2026-06-19

Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.

Keep going

Related & next steps