“My server uses 95% of its memory!” is one of the most common worries, and often there’s no problem at all. Linux deliberately fills unused RAM with file cache and gives it back instantly when applications need it. The number that matters is available memory, plus whether the system is swapping or killing processes. Here’s how to tell real memory pressure from healthy caching, and what to do about it.
Step 1: read memory correctly
free -h
total used free shared buff/cache available
Mem: 3.8Gi 1.6Gi 180Mi 95Mi 2.0Gi 1.9Gi
Swap: 2.0Gi 40Mi 2.0Gi
- free is completely unused RAM. A low number is normal.
- buff/cache is file cache, which can be reclaimed.
- available is what applications can still get. This is the number to watch.
The server above is healthy. 1.9 GB is available, and the cache is speeding up disk reads. See free command.
Real pressure looks like this:
availableunder ~10% of total, and staying there;- swap activity:
si/sonon-zero invmstat 1. See vmstat; - the OOM killer in the logs:
dmesg -T | grep -i 'killed process'. See OOM killer; - the site gets slow and the load average rises with I/O wait.
Step 2: find what’s using memory
ps -eo pid,user,rss,%mem,cmd --sort=-rss | head -15
RSS (resident set size, in KiB) is the physical memory a process uses. Group by program, which helps for many workers:
ps -eo rss,comm --sort=-rss | awk '{a[$2]+=$1} END {for (p in a) printf "%8.0f MB %s\n", a[p]/1024, p}' | sort -rn | head
Shared memory (PHP-FPM workers sharing OPcache, for example) is counted in each RSS, so the totals overstate real use. smem -tk gives PSS, a fairer split.
Containers:
docker stats --no-stream
systemd services, with cgroup totals:
systemd-cgtop -m
Step 3: fix the usual suspects
PHP-FPM
Each worker uses 30–100+ MB, and pm.max_children multiplies that. Too many children is the number one cause of memory exhaustion on WordPress servers. Calculate it from available RAM, and consider pm = ondemand on small servers. See PHP-FPM tuning.
MySQL / MariaDB
innodb_buffer_pool_size is usually the largest single allocation. On a server that also runs the web stack, keep it at roughly 25–40% of RAM, not the 70–80% that’s recommended for dedicated database servers. Watch per-connection buffers × max_connections too. See InnoDB buffer pool and MySQL tuning.
Java (Tomcat, Elasticsearch, Minecraft)
The JVM takes what -Xmx allows, and more for metaspace and threads. Always set -Xmx (and often -Xms) explicitly, and leave headroom for the OS. Elasticsearch: heap ≤ 50% of RAM.
Redis
Set maxmemory and an eviction policy, or a cache grows until the server runs out. See Redis maxmemory.
Node.js / Python apps
A steady climb over hours or days with no matching traffic growth is a memory leak. Watch it:
watch -n 60 'ps -o rss= -p PID'
Fix the code: unbounded caches, global arrays and listeners that are never removed. As a safety net, restart automatically above a limit (PM2 max_memory_restart, or systemd MemoryMax= with Restart=on-failure).
Too many services on one box
A 2 GB VPS running Nginx, PHP, MySQL, Redis, Elasticsearch and a mail server will struggle. Remove what you don’t need, or move heavy components (search, databases) to their own servers.
Step 4: add a safety net
- Swap (1–2 GB) absorbs short spikes and holds idle pages. See swap.
- zram gives compressed swap in RAM, which is effective on small plans. See zram.
- Memory limits per service, so one runaway app can’t take down the database:
MemoryMax=in systemd,-min Docker. See systemd unit files. - Monitoring and alerts on available memory and swap activity. See Netdata.
Don’t do this
echo 3 > /proc/sys/vm/drop_cacheson a schedule. It throws away useful cache and makes the disk work harder. The memory is “used” again within minutes anyway. It’s only for benchmarking.- “RAM cleaner” scripts. Same problem.
- Disabling swap entirely on small servers, which makes OOM kills more likely.
Step 5: when to add RAM
If, after tuning, available memory regularly drops below 10–15% under normal load, or swapping is continuous, the workload needs more memory. As a rough guide:
| Workload | Comfortable RAM |
|---|---|
| Static sites, small apps | 1–2 GB |
| WordPress / LEMP, a few sites | 2–4 GB |
| WooCommerce, several busy sites | 4–8 GB |
| Databases, Elasticsearch, Java apps | 8 GB+ |
See how much RAM does my VPS need. VPS.DO plans go from 2 GB to 40 GB of RAM, and upgrades are requested through a support ticket.
Quick reference
| Task | Command |
|---|---|
| Overview | free -h (watch “available”) |
| Swapping? | vmstat 1 (si/so) |
| Top processes | ps -eo pid,rss,cmd --sort=-rss | head |
| Per service | systemd-cgtop -m |
| Containers | docker stats |
| OOM kills | dmesg -T | grep -i 'killed process' |
Related: high CPU usage and I/O wait.