38.1 shared_buffers: The Primary Data Cache
Right, let’s talk about shared_buffers. This is the big one, the Grand Central Station of your PostgreSQL instance. It’s the chunk of memory your database server sets aside to cache data. Think of your database as having to fetch pages of data from disk, which is agonizingly slow. shared_buffers is its own personal, super-fast, in-memory workspace where it keeps the pages it’s actively using or thinks you’ll need soon. Getting this right is less about a magic formula and more about understanding the traffic patterns in your system.
The most common mistake I see—the one that makes me sigh audibly—is the old “set it to 25% of total RAM!” advice, copy-pasted from a blog post circa 2012. On a modern server with 128GB of RAM, that would be 32GB. That is almost certainly way too high and can actually hurt your performance. Why? Because your operating system also has a brilliant, sophisticated disk caching mechanism. By grabbing a massive 32GB chunk of RAM for itself, PostgreSQL is essentially duplicating the OS’s job, and now you have two separate caches competing for memory instead of cooperating. The kernel is excellent at caching; let it do its job.
How Much Memory Should You Actually Allocate?
So, what’s the modern guidance? Start conservative. On a dedicated database server, a good starting point is 15% of total RAM, but with an absolute ceiling of usually 8-16GB. Even on massive machines, I rarely see compelling evidence to go beyond 16GB for shared_buffers itself. The rest of the RAM is far better used for the OS cache and, crucially, for PostgreSQL’s work_mem and the various other memory areas. The goal is to use shared_buffers for the hot, most frequently accessed data and let the OS handle the broader, colder caching duties.
Let’s be practical. You don’t guess; you look. Connect to your database and see what’s happening right now.
SELECT
name,
setting,
unit,
pg_size_pretty(setting::bigint * block_size::bigint) as pretty_size
FROM pg_settings, (SELECT current_setting('block_size')::integer AS block_size) AS bs
WHERE name = 'shared_buffers';
This will show you your current setting in a human-readable format. Now, to change it, you head to your postgresql.conf file. This isn’t a dynamic setting; you can’t change it on the fly. You must restart the PostgreSQL service.
# Find your postgresql.conf location (output will show the config_file path)
psql -c "SHOW config_file;"
# Edit the file, usually with sudo and your editor of choice
sudo nano /etc/postgresql/15/main/postgresql.conf
# Find the shared_buffers line, uncomment it, and set it.
# Let's say you have 32GB of RAM. 15% is about 5GB. We'll round to 5120MB.
shared_buffers = 5120MB
# Save, exit, and restart PostgreSQL
sudo systemctl restart postgresql
The Perils of Setting It Too Low
Okay, so we’re terrified of setting it too high. What about too low? This is also bad, but in a more obvious way. If shared_buffers is tiny, your database is constantly evicting pages it might need again soon, forcing it to go back to disk (or the OS cache) to re-read them. This shows up as a high rate of buffer cache misses. You can check this with:
SELECT
datname,
blks_hit,
blks_read,
round(blks_hit * 100.0 / (blks_hit + blks_read), 2) AS cache_hit_ratio
FROM pg_stat_database
WHERE datname NOT LIKE 'template%';
A cache hit ratio consistently below 99% for your main database under normal load is a good sign you might need to nudge shared_buffers up a bit. But remember, a query doing a large sequential scan might legitimately pull data directly from the OS cache, so don’t panic over a single dip.
It’s Not a Silver Bullet
Here’s the crucial insight: shared_buffers is not a magic “make my database fast” knob. Its primary benefit is in reducing the physical I/O needed for random access patterns—the kind created by indexed lookups and OLTP workloads. If your entire workload is giant sequential scans (which is rare), a huge shared_buffers won’t help much; the OS cache will handle that just fine. Tuning this parameter is about giving your database an efficient workspace, not building a RAM disk. Set it sensibly, monitor the results, and spend your tuning energy on your queries and indexes, where the real wins are hiding.