How Much RAM Does a Server Need? Measured, Not Guessed (2026)

How Much RAM Does a Server Need? Measured, Not Guessed (2026)

How much RAM does a server need? The honest answer: less than most sizing tables claim — and you cannot calculate it without measuring. Because the number almost everyone treats as “memory usage” is the wrong one. We measured this in 2026 across three of our own production machines: a 24 GB development server, an 8 GB VPS, and a 64 GB Shopware production box.

The finding that overshadows everything else: on the Shopware server, 43 PHP workers add up to 11,245 MB of RSS. Their actual memory consumption is 893 MB. Size your machine with the usual “add up RSS” method and you buy twelve times what the workload needs.

How much RAM does a server need: the short answer

If you take away one line, take this one: read available, not free, and sum PSS, not RSS. Everything else in this article is the justification.

As rough starting points, each backed by numbers we prove below:

Use caseRAMWhat we base this on
Pure reverse proxy / static sites1 GBCaddy with 30+ domains: 104 MB including TLS
Small VPS, 2–3 Node services + SQLite2 GBOur services measure 33–112 MB each
Typical web server with database4 GBHelsinki runs on 8 GB with 4.5 GB free
Node/Next.js apps, several services8 GBOne Next.js process here: 205–287 MB
Shop under real load (Shopware, Magento)16 GB and upThe database alone wants 24 GB
Local AI models32 GB+See our article on running AI locally

This table is a starting point, not a result. The real point: after two weeks of operation you can answer the question yourself — better than any table, because your load is your load.

Mistake one: “free” is the wrong column

The classic. Someone looks at free -m, sees little free memory, and orders more RAM. Here is the actual state of our development server at measurement time:

               total        used        free      shared  buff/cache   available
Mem:           23456        6125        1380          78       16428       17331

1,380 MB free out of 23,456 MB. That looks like 94 % utilization. Panic would be the obvious reaction — and completely wrong. The correct column is on the far right: 17,331 MB available. That is 74 % of the machine.

The difference is those 16,428 MB under buff/cache. That is the page cache: files the kernel keeps in memory because otherwise it would leave that memory idle. This memory is not consumed, it is lent out. The moment a program needs it, it gets it.

The proof: drop the cache and watch

We did not assume this, we measured it. Created a 1.5 GB file, dropped caches, read it twice:

Momentfreebuff/cacheavailable
Before1,205 MB16,612 MB17,340 MB
After drop_caches17,243 MB844 MB17,610 MB
After 1st read (from disk)15,608 MB2,344 MB17,476 MB
After 2nd read (from cache)15,560 MB2,345 MB17,428 MB

Two things live in that table:

First: free jumped from 1,205 to 17,243 MB — a fourteenfold increase. Meanwhile available moved from 17,340 to 17,610 MB, about 1.5 %. The machine had the same usable memory the entire time. Only one of the two columns knew it.

Second — and this is the price: the first read took 1.18 seconds, the second 0.24 seconds. The cache was nearly five times faster. Anyone “freeing memory” by dropping caches buys a prettier number in free and pays for it in disk reads.

Abstract illustration of flowing blue waves and a winding river between mountains under a warm sun

Rule of thumb: a Linux server with lots of free memory is not using its memory. free near zero is not an alarm, it is a working system.

Mistake two: VSZ is not a memory measurement

ps shows two columns that look like memory: VSZ (virtual size) and RSS (resident, actually in RAM). How far apart they can drift is shown by our MariaDB instance on the development server:

MetricValue
VSZ8,592,218,816 kB = 8.00 TiB
RSS24,368 kB = 23.7 MiB
Ratio352,602 ×

Eight terabytes of virtual address space on a machine with 24 gigabytes of RAM. That is not a bug and not a memory leak — it is how modern programs handle addresses. The cross-check via smaps_rollup confirms the real portion:

Rss:               24368 kB
Pss:               24364 kB
Private_Dirty:     15508 kB
Private_Clean:      8856 kB

Actually in RAM: 23.7 MiB, of which 15 MiB genuinely belongs to this database alone.

Why this happens — reproduced in four steps

So this does not stay theoretical, we wrote a small program that requests 2 GiB and then uses it piece by piece:

StepVSZRSS
Start19.1 MiB11.4 MiB
After malloc(2 GiB)2,067.1 MiB11.6 MiB
After writing 256 MiB2,067.1 MiB267.6 MiB
After writing 1 GiB2,067.1 MiB1,035.6 MiB
After free()19.1 MiB11.6 MiB

The second row is the entire point: requesting 2 GiB raised RSS by 0.2 MiB. The kernel did nothing but make a promise. Only writing to a page costs real memory — and then exactly as much as was written.

A clear glass bottle standing beside a glowing cube on a teal surface

This also explains why Committed_AS on our server sits at 20.2 GiB while CommitLimit is 15.5 GiB. Linux promised more than it has, because it knows nobody claims everything at once. This is called overcommit, and ours runs at the default vm.overcommit_memory = 0.

Mistake three — the expensive one: adding up RSS

Here is where most sizing exercises fall apart. The obvious method: add the RSS of all processes, add headroom, done. On our development server that yields:

MetricValue
Sum of all RSS8,385 MiB
Sum of all PSS5,406 MiB
free -m reports “used”6,081 MiB
Double-counted2,978 MiB

Nearly three gigabytes of air in a calculation that looks perfectly correct. The reason: RSS counts shared pages in full for every process. A library used by 40 processes appears forty times. PSS (Proportional Set Size) divides such pages among their users and is therefore the only sum you are allowed to build.

On the production server this becomes a factor of 12.6

On the development server it is a 55 % overshoot. On the Shopware server, with its many identical PHP processes, it becomes something else entirely:

MetricValue
php-fpm workers43
Sum of RSS11,245 MiB
Average per worker (RSS)261 MiB
Sum of PSS (real)893 MiB
Overestimation12.6 ×

43 workers share the same PHP interpreter, the same extensions, the same OPcache. Each one reports the entire shared foundation as “its” memory. Apply the rule of thumb “261 MB per worker × planned workers” and you size a machine for 11 GB where 0.9 GB is needed.

How to measure PSS yourself:

# Real consumption of a process family (php-fpm here)
T=0; for p in $(pgrep -f php-fpm); do
  v=$(awk '/^Pss:/{print $2; exit}' /proc/$p/smaps_rollup 2>/dev/null)
  T=$((T+${v:-0}))
done; echo "Total PSS: $((T/1024)) MiB"

And to cross-check the grand total:

ps -eo rss --no-headers | awk '{s+=$1} END {print "RSS total: "int(s/1024)" MiB"}'

If the two numbers diverge sharply, you have many identical processes — and the RSS figure is worthless for sizing.

Isometric 3D rendering of various server racks on a raised platform connected by glowing circuit lines

What individual services actually need — our numbers

Instead of copying rules of thumb, here are the systemd-measured values of our running services. This is the cgroup figure, so it includes all child processes and associated cache:

ServiceMemoryNote
fail2ban23 MBPython, still does its job
postgresql@1623 MBwithout notable load
podcast-studio (Node)33 MBsmall service
nyx-analytics (Node)42 MBanalytics + SQLite
nyxvault (Node)68 MBencryption + uploads
shellgames (Node)104 MBWebSockets, several games
caddy104 MB30+ domains, TLS, reverse proxy
veganmaps-api112 MBExpress + SQLite, real users
docker179 MBdaemon alone, no containers
signal-cli214 MBJVM

Two readings from this:

A reverse proxy is cheap. Caddy serves over thirty domains with automatic TLS for 104 MB. If all you need is static sites and forwarding, 1 GB total RAM will do. Details in our article on Nginx as a reverse proxy and the introduction What is a reverse proxy?.

The JVM is the most expensive line in the list. signal-cli alone needs more than six of our Node services combined. Language runtime beats feature set.

And the containers alongside, measured with docker stats:

ContainerMemory
forkcart-db5.9 MB
cobalt-api7.7 MB
woo-proxy-db22.7 MB
pasta-stage-6740.8 MB
shopware-test165.6 MB
woo-proxy-wp221.9 MB

Six containers together: 465 MB. The Docker daemon itself costs more at 179 MB than the four smallest containers combined. If you run Docker, budget the daemon as its own line item — it is there even when nothing runs. How we operate containers is covered in our Docker Compose guide.

The one service where rules of thumb hold: the database

On the Shopware production server the ranking looks completely different:

ProcessRSS
mariadbd34,237 MB
redis-server342 MB
php-fpm (single)~285 MB

34 GB for the database on a 64 GB machine. And unlike php-fpm, this number is real — the InnoDB buffer pool is configured to 24 GiB, and that is precisely a database’s purpose: hold data in RAM instead of reading it from disk.

This is the most important distinction in the whole article: for application processes, high memory is often just a measurement illusion. For databases, it is the entire point. The buffer pool is not waste to be optimized away — it is why the shop is fast. Redis reports 263 MB used against a maxmemory of 8 GB, so it still has plenty of headroom.

Swap: not a last resort, but offloading

Swap has a bad reputation rooted in a false idea: “swap is what happens when RAM runs out.” Our measurement says otherwise. The development server:

Swap:           4095        3949         146

3,949 of 4,095 MB of swap in use — while 17,331 MB of RAM is available. Under the last-resort theory this should not exist. The reason sits in vm.swappiness = 60: the kernel pages out memory that has not been touched in a long time to make room for page cache — even when RAM is free. That is not a shortage, that is housekeeping.

Who is in swap on our machine?

ProcessSwapped out
node748 MB
mysqld405 MB
mysqld348 MB
next-server247 MB
java231 MB
dockerd213 MB
gpg-agent117 MB

These are mostly processes that initialized a lot at startup and have used only a fraction since. Parking those pages on disk is the right call.

Abstract composition of layered flowing waves in blue, teal and orange tones accented with golden glittering particles

The number that actually matters: memory pressure

Whether swap is a problem is told neither by its size nor its fill level. It is told by PSI (Pressure Stall Information), available since kernel 4.20:

cat /proc/pressure/memory

Our three machines compared:

MachineRAMsome avg60Verdict
Development server24 GB0.23completely relaxed
Helsinki (VPS)8 GB0.00no pressure at all
Shopware production64 GB0.00no pressure at all

The some avg60 value states what share of the last 60 seconds at least one process had to wait for memory. The rule of thumb from our operations:

  • below 1 — fine, no action needed
  • 1 to 10 — it is starting to stutter, watch it
  • above 10 — real shortage, add RAM now
  • full persistently above 0 — the system can no longer keep up

The development server is the loudest of the three — at 0.23. Meaning: even the machine whose swap is 96 % full has virtually no memory pressure. A full swap is not an alarm. A high PSI value is.

The OOM killer: what happens when it really does run out

When Linux genuinely cannot find memory anymore, the out-of-memory killer steps in. It terminates a process — not necessarily the guilty one, but the one with the highest score derived from memory usage and oom_score_adj. In practice it often hits the database, because it holds the most memory.

On our machines this has not happened once in 82 days of uptime:

journalctl -k | grep -ci "out of memory\|oom-kill"
# → 0

How to check whether it hit you:

# Search the kernel log for OOM events
journalctl -k --no-pager | grep -i "oom-kill\|Out of memory: Killed"

# How exposed is a given process?
cat /proc/$(pgrep -x mariadbd)/oom_score

You can protect an important service by giving it a lower score via systemd — then someone else gets hit instead:

# /etc/systemd/system/myservice.service.d/oom.conf
[Service]
OOMScoreAdjust=-500

A detail many overlook: systemd-oomd is inactive on our system. So there is no second instance intervening ahead of the kernel. Check yours with systemctl is-active systemd-oomd — if it is active, it can terminate services before the kernel is even under pressure, based on PSI values.

The line item almost every calculation forgets: the kernel itself

Before the first application starts, the operating system has already helped itself. On our development server:

Kernel structureMemory
Slab (kernel objects)533 MB
PageTables174 MB
VmallocUsed51 MB
KernelStack28 MB
Total767 MiB

Three quarters of a gigabyte before anything useful runs. Of that, 2,568 MB of the slab portion is reclaimable as SReclaimable — but the rest is fixed. On top of that our journal occupies 1,008.9 MB on disk, which costs no RAM but is happily forgotten during sizing.

Practical consequence: on a 1 GB VPS, after kernel and base services you realistically have 500–600 MB left for your application. That is why 1 GB only makes sense for genuinely lean workloads.

The three machines side by side

All values collected on the same day:

DevelopmentHelsinki (VPS)Shopware production
Total RAM23,456 MB7,745 MB62,792 MB
used6,125 MB3,205 MB36,428 MB
free1,380 MB853 MB4,239 MB
buff/cache16,428 MB4,004 MB23,109 MB
available17,331 MB4,540 MB26,363 MB
Utilization (real)26 %41 %58 %
Swap in use3,949/4,095 MB1,203/2,047 MB8/16,383 MB
PSI some avg600.230.000.00
CPU cores1216

The most interesting row is the swap row. The production server has 16 GB of swap configured and uses 8 MB of it — 0.05 %. The development server uses 96 % of its swap. Both report PSI values near zero. This shows very clearly: swap usage does not correlate with memory shortage. The production server simply has so much RAM that the kernel never had a reason to page anything out.

And one more thing: the production server is the most heavily loaded machine at 58 % — and still reports zero memory pressure. 58 % is a very healthy operating point. Running servers at 20 % utilization means paying for capacity you never call on.

How to answer the question for your own server

Here is the procedure we use ourselves. It needs two weeks of operation and five commands.

Step 1 — read the right column:

free -m
# Only 'available' counts. Ignore 'free'.

Step 2 — observe memory pressure over time:

cat /proc/pressure/memory
# 'some avg300' below 1 = all good

Step 3 — determine real consumption per service:

systemctl status SERVICENAME | grep Memory
# or for all at once:
systemd-cgtop -m --iterations=1

Step 4 — check the process-family trap:

# RSS total (overestimates) against PSS total (real)
ps -eo rss --no-headers | awk '{s+=$1} END {print "RSS: "int(s/1024)" MiB"}'
T=0; for f in /proc/[0-9]*/smaps_rollup; do
  v=$(awk '/^Pss:/{print $2; exit}' "$f" 2>/dev/null); T=$((T+${v:-0}))
done; echo "PSS: $((T/1024)) MiB"

Step 5 — peaks instead of snapshots:

A single free -m shows you one second. What matters is the worst moment of the last two weeks. For that you need a recording — either sar from the sysstat package or a simple cron line:

*/5 * * * * echo "$(date +\%F\ \%H:\%M) $(awk '/MemAvailable/{print $2}' /proc/meminfo)" >> /var/log/mem-history.txt

After two weeks you know how low available actually dropped. That number, not the table above, answers your question.

The rule we use for upgrading

We add RAM when one of these conditions holds for several days:

  1. available drops below 15 % of total memory
  2. PSI some avg300 stays above 1
  3. The OOM killer fired — even once
  4. The database cannot size its buffer pool to fit the hot data

What is not a reason to upgrade: a low free value, a full buff/cache, used swap, or a high RSS total.

What we deliberately do not claim

Honesty includes saying where our measurements stop.

We did not measure desktop or VM scenarios. Our machines lack /dev/kvm; we cannot run virtual machines and therefore cannot measure how ballooning or KSM deduplication behave across several VMs. If you are considering virtualization, our Proxmox vs ESXi comparison is a starting point — but the RAM figures there are not ours.

We do not run zram. None of the three machines uses compressed swap in RAM. So we cannot say from experience how much it helps on small VPS instances. The technique is real and frequently recommended — you will get no number for it from us, because we have none.

We did not touch Windows Server. All values come from Linux systems.

A copied number looks identical in a table to a measured one. That is why there is a gap here rather than an estimate.

Conclusion: how much RAM does a server need?

A blue balance scale holding stacks of tech drive modules on both sides against an orange backdrop

The question of how much RAM a server needs cannot be read off a table, because the common measurement methods are systematically wrong in one direction: they overestimate. RSS counts shared pages multiple times, VSZ measures promises rather than consumption, and free treats useful cache as lost memory.

The three numbers that stick from our measurements:

  • 12.6 × — how badly the RSS total overestimates 43 PHP workers
  • 352,602 × — how far VSZ and RSS diverge on our MariaDB
  • 0.23 — the highest memory pressure across three production machines, meaning practically none

In practice: buy small, measure for two weeks, upgrade when available and PSI tell you to. A 4 GB server running at 58 % utilization with zero memory pressure is correctly sized. A 32 GB server at 20 % utilization is a standing order to your hosting provider.

The one exception remains the database. There, memory is not consumption but performance: the buffer pool should be large enough that the actively used data fits inside. Anything beyond that is wasted, anything below costs you disk reads on every request.

If you are setting up a new server, our guide on setting up a Linux server will help; for choosing a distribution, see our overview of Linux server distributions. And if you are considering running AI models yourself — that is the one case where the memory question is genuinely decided before purchase: running AI locally.

Frequently asked questions

What is the minimum RAM a server needs?

A pure reverse proxy or web server for static sites runs stably on 1 GB. Our Caddy serves over 30 domains with TLS for 104 MB. But budget for the fact that kernel and base services already occupy 767 MB of fixed structures on our system — on a 1 GB VPS you realistically have 500 to 600 MB left for your own application.

Why does my server show almost no free memory?

Because Linux uses unused memory as page cache. Our development server reported 1,380 MB free but 17,331 MB available — the difference is 16,428 MB of file cache that is returned at any time. Always look at the available column, not free. A server with lots of free memory simply is not using its memory.

What is the difference between VSZ and RSS?

VSZ is the virtual address space, meaning everything a process has requested. RSS is the portion actually resident in RAM. On our MariaDB, 8.00 TiB of VSZ stands against 23.7 MiB of RSS — a ratio of 352,602. For sizing purposes VSZ is completely useless.

Can I just add up the RSS values of all processes?

No, and this is the most expensive sizing mistake there is. RSS counts shared memory pages in full for every process. With 43 php-fpm workers on our production server the RSS total came to 11,245 MB while actual consumption was 893 MB — an overestimation by a factor of 12.6. Sum PSS from /proc/PID/smaps_rollup instead.

Is used swap a sign of insufficient RAM?

No. Our development server has 96 % of its swap in use while simultaneously having 17 GB of available RAM. At vm.swappiness = 60 the kernel pages out rarely used memory to make room for cache. Whether there is a real shortage is told by /proc/pressure/memory — not by the swap fill level.

How do I know my server really needs more RAM?

Three signals: available drops persistently below 15 % of total memory, the PSI value some avg300 in /proc/pressure/memory exceeds 1, or the OOM killer fired. Across our three machines the highest PSI value was 0.23, and in 82 days of uptime there were zero OOM events.

How much RAM does a server need for an online shop?

Budget from 16 GB upward, and the largest line item is the database. On our Shopware production server, MariaDB occupies 34 GB, including a configured InnoDB buffer pool of 24 GiB. Unlike application processes, this memory is not a measurement error but the purpose: data in RAM instead of on disk. The 43 PHP workers alongside need only 893 MB combined.

What happens when a server runs out of memory?

The kernel invokes the OOM killer and terminates a process — not necessarily the culprit, but the one with the highest score from memory usage and oom_score_adj. It often hits the database. Protect important services via systemd with OOMScoreAdjust=-500. Also check whether systemd-oomd is active — it intervenes ahead of the kernel.

How much RAM does Docker use?

The Docker daemon alone occupies 179 MB on our server before a single container runs. Our six production containers total 465 MB, ranging from 5.9 MB for a small database to 221.9 MB for a WordPress instance. Plan the daemon as its own line item.

Should I clear the cache to free up memory?

No. We measured it: after drop_caches, free jumped from 1,205 to 17,243 MB, but available changed by only 1.5 % — so no usable memory was gained. You paid for it anyway: reading the same file afterwards took 1.18 instead of 0.24 seconds, nearly five times slower.