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 same sum PostgreSQL does at startup in CalculateShmemSize(): buffers and their descriptors, lock tables, SLRU caches, WAL buffers, per-backend arrays. Useful when you want to size a configuration you are not running yet — a bigger shared_buffers, or a server you have not built.
shared_buffers plus about 2 % of overhead, and everything else shifts it by a few pages. So padding vm.nr_hugepages by 10 % "just in case" is not caution — it is gigabytes taken from the page cache to cover a number the server already knows exactly. Take the real value with SHOW shared_memory_size_in_huge_pages, add a percent or two, and recompute when the configuration changes.—
huge_pages applies to. Memory allocated later per backend (work_mem, maintenance_work_mem, catalog and plan caches) is not in it, and does not use huge pages. So do not add anything to the reservation for it.Twelve configurations run through the server itself on PostgreSQL 17, across 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 inputs put through the calculator. Two of them also load pg_stat_statements; everything else stays at its default.
What is compared is the requirement itself, shared_memory_size_in_huge_pages. The margin slider is left out: postgres -C reports what the server needs, while the vm.nr_hugepages line is that number plus whatever slack you add on top.
$ 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 …
| Configuration | calculator | postgres -C | Δ | Δ memory |
|---|---|---|---|---|
| shared_buffers 8 GB · max_connections 100 · locks 64 | 4 217 | 4 218 | −0.02 % | −2 MB |
| shared_buffers 8 GB · max_connections 10 000 · locks 256 | 4 825 | 4 826 | −0.02 % | −2 MB |
| shared_buffers 32 GB · max_connections 100 · locks 64 | 16 784 | 16 788 | −0.02 % | −8 MB |
| shared_buffers 32 GB · max_connections 1 000 · locks 128 | 16 817 | 16 821 | −0.02 % | −8 MB |
| shared_buffers 32 GB · max_connections 10 000 · locks 256 | 17 392 | 17 396 | −0.02 % | −8 MB |
| shared_buffers 32 GB · max_connections 300 · pg_stat_statements max 5000 | 16 790 | 16 794 | −0.02 % | −8 MB |
| shared_buffers 128 GB · max_connections 500 · locks 64 | 67 063 | 66 975 | +0.13 % | +176 MB |
| shared_buffers 128 GB · max_connections 5 000 · locks 256 | 67 356 | 67 268 | +0.13 % | +176 MB |
| shared_buffers 128 GB · max_connections 1 000 · pg_stat_statements max 10 000 | 67 075 | 66 988 | +0.13 % | +174 MB |
| shared_buffers 512 GB · max_connections 1 000 · locks 128 | 268 159 | 267 352 | +0.30 % | +1 614 MB |
| shared_buffers 1 TB · max_connections 100 · locks 64 | 536 224 | 534 457 | +0.33 % | +3 534 MB |
| shared_buffers 1 TB · max_connections 10 000 · locks 256 | 536 832 | 535 065 | +0.33 % | +3 534 MB |