When a server runs out of RAM and swap, the kernel's OOM killer picks a process and kills it to keep the system alive. Often the victim is the largest process, MySQL, which looks like a random database crash. This guide shows how to confirm an out-of-memory kill and stop it from recurring.
Before you start
- Root access to the VPS.
- The approximate time when a service stopped or a site went down.
Confirm an OOM kill
dmesg -T | grep -iE 'out of memory|killed process'journalctl -k --since "2 days ago" | grep -i oomA typical line:
Out of memory: Killed process 1234 (mysqld) total-vm:3800000kB, anon-rss:2100000kBThe anon-rss value is the memory that process really used. Services managed by systemd also show status=9/KILL or oom-kill in systemctl status.
Find out what used the memory
The killed process is not always the culprit. The kernel log prints a table of processes at the moment of the kill; look for many copies of the same program (dozens of php-fpm or node workers) or one process with a huge RSS. Right now:
ps aux --sort=-rss | head -15free -hGroup memory per program to spot worker pools:
ps -eo rss,comm --no-headers | awk '{m[$2]+=$1} END {for (p in m) printf "%8.0f MB %s\n", m[p]/1024, p}' | sort -nr | headCommon causes and fixes
| Cause | Fix |
|---|---|
| Too many PHP-FPM workers | Lower pm.max_children to fit RAM |
| MySQL buffer pool too large | Reduce innodb_buffer_pool_size and max_connections |
| Node.js memory leak | Fix the leak; meanwhile PM2 max_memory_restart |
| Traffic spike or bot flood | Rate limiting, caching, blocking abusive IPs |
| Backups or imports at peak time | Schedule at night, lower priority |
| No swap at all | Add a small swap file as a cushion |
| Plan simply too small | Upgrade RAM |
Budget memory per service
Add up the worst case: MySQL buffer pool plus per-connection buffers, PHP-FPM max_children × average worker size, Redis maxmemory, Node processes and the OS (300–500 MB). Keep the total below physical RAM, with swap only as a safety net.
Protect important services
- Make the OOM killer prefer other processes by giving a service a lower score, e.g. for MySQL with
systemctl edit mysql:[Service] OOMScoreAdjust=-500 - Cap noisy services instead, so they cannot take the whole server:
[Service] MemoryMax=1G - Ensure services restart automatically:
Restart=on-failure.
Lowering a score only moves the problem to another process; the real fix is reducing total memory use.
Check the result
- Watch
free -handvmstat 5during busy hours: available memory should stay comfortably above zero. - Set up memory alerts (Netdata or another monitor) so you hear about pressure before the next kill.
Troubleshooting
- Kills in a container or VPS but plenty of free RAM shown: a cgroup limit (Docker
mem_limit, systemdMemoryMax, or the VPS plan limit) was reached, not host RAM. - Server freezes instead of killing: heavy swapping (thrashing). Reduce memory use or swappiness.
- Cannot allocate memory when starting programs: the same root cause; free memory first.
OOM killer FAQ
Should I disable the OOM killer?
No. Without it the whole server can hang. Fix memory usage instead.
Does swap prevent OOM kills?
It delays them and absorbs short spikes; it does not fix sustained over-use.
Can this happen on cPanel hosting?
On shared hosting, CloudLinux enforces per-account memory limits instead, which shows as resource limit errors.
