PostgreSQL · shared memory · hugetlb · sizing

How many huge pages
does PostgreSQL actually need?

Since PostgreSQL 15 the server answers that itself: shared_memory_size_in_huge_pages. And the answer is dominated by one setting — as a rule of thumb, shared memory is shared_buffers plus about 2 % of overhead for describing it, plus a per-backend tail. Everything else is a footnote until you make it extreme, which is exactly why the sizing is boring and the failure modes are not. This page computes that number for a configuration you do not run yet, and answers the question the docs skip — how much margin to put into vm.nr_hugepages, and why the honest answer is "not much".

THE CALCULATOR

Put in your own numbers

The same arithmetic, reimplemented from the source: the per-buffer structures, the lock and predicate-lock hash tables (through dynahash's real hash_estimate_size()), the SLRU caches with their auto-tuning, WAL buffers, the per-backend arrays. The server does the same sum in CalculateShmemSize() and then reports size_b / huge_page_size + 1 — integer division, with one page added back for the remainder. Useful when the configuration you want to size for is not the one currently running — a bigger shared_buffers, or a server you have not built yet.

What this is for. An illustration, not an oracle. Use it to watch each GUC move the requirement, and to settle the two things that actually matter: the number is shared_buffers plus roughly 2 % of overhead, and everything else shifts it by a handful of pages. Which is the whole argument against padding vm.nr_hugepages by 10 % or more "just in case" — that is not caution, it is a few gigabytes taken away from the page cache to cover a number the server already knows exactly. Take the real value from the running instance with SHOW shared_memory_size_in_huge_pages, add a percent or two, and recompute when the configuration changes.
Configuration
2 MB pages
32 GB
300
64
2 %
everything else that counts
max_worker_processes
max_wal_senders
autovacuum_max_workers
max_prepared_transactions
max_pred_locks_per_transaction
track_activity_query_size (bytes)
wal_buffers (MB, 0 = auto, server max 2047)
min_dynamic_shared_memory (MB)
transaction_buffers (8 kB blocks, 0 = auto)
shared_memory_size_in_huge_pages
shared_memory_size
reserve — vm.nr_hugepages
hover a row for the matching pg_shmem_allocations names
what to write on the machine
Only the main segment. This number covers the one anonymous mapping the postmaster creates at startup — the one huge_pages applies to. Memory allocated later per backend (work_mem, maintenance_work_mem, catalog and plan caches) and the DSM segments a parallel query creates on the fly are not in it, and they do not use huge pages. The one exception is min_dynamic_shared_memory: that area is carved out of the main segment, so it is both counted here and backed by huge pages.
THE CHECK

Calculator versus postgres -C

Ten configurations run through the server itself on PostgreSQL 17, sweeping the full range the sliders allow — shared_buffers from 8 GB to 1 TB, max_connections from 100 to 10 000, max_locks_per_transaction from 64 to 256 — then the same input put through the calculator. Every other setting stays at its default. The command is the one from the top of the page, and it needs no memory: it computes the number and exits.

What is compared is the requirement — the big number in the calculator, shared_memory_size_in_huge_pages. The margin slider is deliberately left out of it: postgres -C reports what the server needs, while the vm.nr_hugepages line is that number plus whatever slack you choose to add on top.

how the right-hand column was produced
$ postgres -D /tmp/d -C shared_memory_size_in_huge_pages \
      -c shared_buffers=1024GB -c max_connections=10000 -c max_locks_per_transaction=4096 …
Configurationcalculatorpostgres -CΔΔ memory
shared_buffers 8 GB · max_connections 100 · locks 644 2174 218−0.02 %−2 MB
shared_buffers 8 GB · max_connections 10 000 · locks 2564 8254 826−0.02 %−2 MB
shared_buffers 32 GB · max_connections 100 · locks 6416 78416 788−0.02 %−8 MB
shared_buffers 32 GB · max_connections 1 000 · locks 12816 81716 821−0.02 %−8 MB
shared_buffers 32 GB · max_connections 10 000 · locks 25617 39217 396−0.02 %−8 MB
shared_buffers 128 GB · max_connections 500 · locks 6467 06366 975+0.13 %+176 MB
shared_buffers 128 GB · max_connections 5 000 · locks 25667 35667 268+0.13 %+176 MB
shared_buffers 512 GB · max_connections 1 000 · locks 128268 159267 352+0.30 %+1 614 MB
shared_buffers 1 TB · max_connections 100 · locks 64536 224534 457+0.33 %+3 534 MB
shared_buffers 1 TB · max_connections 10 000 · locks 256536 832535 065+0.33 %+3 534 MB
Which way it errs. On pools up to 32 GB the calculator lands a few pages under the server; from 128 GB up it goes over — +0.13 % at 128 GB, +0.33 % at 1 TB, which is 3.5 GB worth of pages on a terabyte pool. The error follows the pool size, not the connection count.