← Back to blog

PostgreSQL dedicated server: sizing RAM, NVMe and replicas

Maria Ilinca Bostan

A PostgreSQL dedicated server should have enough RAM to hold the hot part of your data in memory, fast mirrored NVMe or SSD for the data and the write ahead log, and a replica on a second machine for failover. A Single EPYC 9254 with 128 GB of DDR5 and two NVMe drives suits most production databases up to a few hundred gigabytes.

This guide sizes memory, disks and cores with worked numbers, gives the settings that matter on a machine with lots of RAM, covers replication and backups, and lists the servers that fit each size of database.

What does a PostgreSQL dedicated server need?

PostgreSQL keeps its tables and indexes in 8 kB pages on disk. Pages it reads often stay in two caches: its own shared buffers, and the operating system's page cache, which holds whatever file data the kernel has room for. A query that finds its pages in either cache never waits for the disk. A query that misses both does.

That makes RAM the first thing to size, disk latency the second and cores the third. Every write also goes to the write ahead log (WAL) first, and a commit waits until the WAL reaches stable storage, so the speed at which the disk confirms small writes sets a ceiling on commits per second.

Bare metal suits a database well. Nothing else competes for the cache, the memory bandwidth or the disks, so query times stay steady through the day, and there is no hypervisor between PostgreSQL and the NVMe.

How much RAM does a PostgreSQL server need?

Size RAM to your working set, the tables and indexes your queries touch in a normal hour, not to the total database size. A 400 GB database where most queries read the last three months of orders might have a working set of 60 GB. If that fits in memory with room to spare, most reads never reach the disk.

You can see how well the cache works today with one query:

SELECT datname,
       round(100.0 * blks_hit / nullif(blks_hit + blks_read, 0), 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = current_database();

This counts only hits in shared buffers, so a page served from the operating system cache shows as a read. On a busy OLTP database, a hit rate that falls well below 99 percent under normal load usually means the working set no longer fits in shared buffers.

Settings for a server with 128 GB

The PostgreSQL documentation suggests about 25 percent of RAM for shared_buffers as a starting point on a dedicated database server. The rest is left to the operating system cache, which PostgreSQL relies on too. A sensible first configuration for 128 GB looks like this:

# postgresql.conf, 128 GB dedicated server
shared_buffers = 32GB
effective_cache_size = 96GB
work_mem = 32MB
maintenance_work_mem = 2GB
huge_pages = try
max_wal_size = 16GB
random_page_cost = 1.1
  • effective_cache_size allocates nothing. It tells the planner how much data is likely to be cached, so it chooses index scans more readily.
  • work_mem applies to every sort and hash step in every query, so a complex query can use several times this amount. Multiply it by your busy connection count before raising it.
  • huge_pages cuts memory management overhead for a large shared_buffers. On Linux you reserve them with vm.nr_hugepages, and PostgreSQL falls back to normal pages if the reservation is too small.
  • max_wal_size controls how often checkpoints happen. The default of 1 GB forces frequent checkpoints on a busy server, and each one flushes dirty pages to disk in a burst.
  • random_page_cost defaults to 4.0, a figure from spinning disks. On NVMe and SSD a value near 1.1 is common, so the planner stops avoiding index scans.

Turn on pg_stat_statements before you tune anything else. It records the time each query shape takes in total, and tuning the top five queries usually does more than any server setting.

NVMe or SSD: how should the disks be laid out?

PostgreSQL mixes two disk patterns. Random reads of 8 kB pages when the cache misses, and small sequential writes to the WAL on every commit. NVMe handles both with lower latency and far more parallel requests than SATA SSD, which shows up most during cache misses, index builds, vacuum and checkpoints.

Mirror the disks. RAID 1 on two drives, or RAID 10 on more, means one failed drive does not take the database down. On a server with many drives you can put the WAL on its own mirrored pair, so commits never queue behind a large scan, but on NVMe a single RAID 10 array usually has enough headroom for both.

PostgreSQL 18 added an asynchronous I/O subsystem, set with io_method, which lets sequential scans, bitmap heap scans and vacuum issue several reads at once. It gains most on storage that answers many requests in parallel, which is what NVMe does well.

A worked example for disk size

Here is the arithmetic for an example database. Replace the inputs with figures from your own system.

StepCalculationResult
Tables and indexes todayfrom pg_database_size()300 GB
Growth over two years15 GB a month × 24360 GB
Size in two years300 GB + 360 GB660 GB
Room for WAL, index rebuilds and bloat660 GB × 1.5990 GB, about 1 TB

The extra half is not waste. Rebuilding an index or repacking a table writes a full new copy before the old one is dropped, WAL piles up when a replica falls behind, and tables carry some dead rows between vacuums. A disk that is 95 percent full turns all three into an outage.

In this example a 1 TB mirror would be full on the day the estimate runs out, so it is time to choose the larger array before ordering, not after.

How many cores does PostgreSQL use?

Each client connection is its own server process, and one query usually runs on one core. Parallel query can add worker processes to large scans, joins and aggregates, two per query by default through max_parallel_workers_per_gather. So a busy OLTP database uses many cores through many connections, while each individual query depends on the speed of a single core.

That favours fast cores over very many slow ones. The EPYC 9254 runs at 2.9 GHz base and up to 4.15 GHz boost with Zen 4 cores, which shortens every query. Our EPYC 9254 guide covers the processor, including why 24 cores suit per core licensed databases.

Connections cost memory, so do not raise max_connections into the thousands. Put PgBouncer in front in transaction pooling mode and keep a few dozen to a few hundred real server connections. Hundreds of idle application connections then share a small pool.

Which dedicated server fits your PostgreSQL database?

Prices are monthly, exclude VAT and include the 1 Gbps guaranteed unmetered port. Usable space assumes RAID 1 on two drives and RAID 10 on more.

ConfigurationCoresRAMStorageUsablePer month
Dual Xeon E5 2690 v4 + 2 × 500 GB SSD28128 GB DDR4 ECC2 × 500 GB SSD500 GB€239
Dual Xeon E5 2690 v4 + 6 × 2 TB SSD28128 GB DDR4 ECC12 TB SSD6 TB€349
Single EPYC 925424128 GB DDR5 ECC2 × 1 TB NVMe1 TB€449
Single EPYC 9254 + 24 × 1 TB NVMe24128 GB DDR5 ECC24 TB NVMe12 TB€969
Dual EPYC 9254 + 24 × 1 TB NVMe48256 GB DDR5 ECC24 TB NVMe12 TB€1,199

For a database under about 300 GB that grows slowly, the Dual Xeon E5 2690 v4 is the economical choice, delivered in about 45 minutes. For the 1 TB of the worked example, the six SSD build gives years of room for €349, while the Single EPYC 9254 with 24 NVMe drives is the pick when cache misses and write latency decide your response times. EPYC servers are built to order, with a delivery estimate of about 14 days.

Every configuration has ECC memory, which matters for a process that holds data in RAM for months. Our guide to ECC memory on a server explains what it catches.

Replication, failover and backups

One server is a single point of failure. Streaming replication sends WAL from the primary to a standby that replays it continuously, so a second server of the same size can take over in seconds to minutes. The standby can also serve read only queries, such as reports.

  • Asynchronous replication is the default. Commits do not wait for the standby, so a failover can lose the last few transactions.
  • Synchronous replication makes each commit wait for the standby, so no confirmed transaction is lost, at the cost of a network round trip per commit. Keep both servers in the same location if you use it.
  • Automatic failover needs a tool that decides which node is primary. Patroni is a common choice, with etcd as a three member store so a majority survives one failure. Two members can run on the database servers and the third on a small VPS.

Replication is not a backup. A dropped table replicates to the standby in moments. Take base backups and archive WAL with pgBackRest or Barman to storage on another server, which gives point in time recovery to any second within the retention window. Then restore it somewhere on a schedule, because a backup that has never been restored is only a hope.

When does PostgreSQL outgrow a VPS?

A VPS suits development and small production databases. Our largest KVM VPS plan, Titan, has 8 vCPU and 32 GB of RAM for €64 a month, and our guide to database servers on a VPS covers sizing at that scale. Move to bare metal when any of these appear:

  • The working set no longer fits in 32 GB and the cache hit rate falls under load.
  • Query times swing at busy hours with no change on your side, and top shows CPU steal.
  • Commit latency rises because the shared disk cannot confirm WAL writes quickly enough.
  • The database needs more than the extra storage a VPS can add.

The database servers use case covers the same move for MySQL, MariaDB and other engines.

Questions people ask

How much RAM do I need for a 500 GB PostgreSQL database?

It depends on the working set, not the total. If queries mostly touch the most recent 10 to 20 percent of the data, 128 GB holds that in memory with room for connections and the operating system. If reports scan everything all day, plan for 256 GB or accept more disk reads.

Should PostgreSQL run on ZFS?

It can, and ZFS snapshots and compression are useful. It also needs tuning, such as a record size that suits 8 kB pages and care with memory, since the ZFS cache competes with shared buffers. Ext4 or XFS on RAID 10 is the simpler default if you have no ZFS experience in the team.

Which port does PostgreSQL use?

Port 5432 by default. Allow it only from your application servers in the firewall, require TLS and scram authentication in pg_hba.conf, and never open it to the whole internet.

How much does a PostgreSQL dedicated server cost?

From €239 a month for the Dual Xeon E5 2690 v4 with mirrored 500 GB SSDs, €449 for the Single EPYC 9254 with mirrored NVMe, and €969 for the EPYC with 24 NVMe drives, excluding VAT. A primary and a standby cost twice the single figure.

Next step

Measure your working set and cache hit rate, run the disk arithmetic with your own growth, and pick the primary from the table. Then order an identical standby in the same location, so failover and restores have somewhere to go from the first day.

Billed monthly · no setup fee

Deploy your first server now.

No minimum term. Intel Xeon builds carry a delivery estimate of under an hour; AMD EPYC builds are made to order. Every configuration has full root, ECC memory and a guaranteed 1 Gbps unmetered port.

$ curl https://my.voxa.host/api/v1/products -H "Authorization: Bearer fbk_…"

Dedicated billed monthly · No setup fee · Full root on every server

Included with every dedicated server
IPv4 address
One included
Unmetered port
1 Gbps guaranteed
DDoS protection
100 Gbps · L3/L4
Root access
Included
Setup fee
€0.00
Minimum term
None