Server Slow: RAM Full or OOM Killer
01
Quick diagnosis
bash
# Real-time memory usage
free -h
# Top processes by RAM consumption
ps aux --sort=-%mem | head -15
# Processes killed by OOM (Out of Memory)
dmesg | grep -i "killed process\|oom"
journalctl -k | grep -i "oom\|killed"
Output of free -h:
total used free shared buff/cache available
Mem: 3.8Gi 3.6Gi 84Mi 12Mi 156Mi 154Mi
Swap: 0B 0B 0B
If available is less than 100-200 MB and swap is 0, system is in crisis.
02
OOM Killer: find the culprit
bash
# Search in logs
journalctl -k --since "1 hour ago" | grep -i oom
# Example output:
# Out of memory: Kill process 1234 (mysql) score 850 or sacrifice child
# Killed process 1234 (mysql) total-vm:512MB, anon-rss:480MB
03
Identify what consumes RAM
bash
# Top 10 processes by RSS (physical memory)
ps aux --sort=-%rss | awk 'NR<=11{printf "%-10s %-10s %s\n", $1, $3" "$4, $11}' | head -11
# Memory per systemd service
systemd-cgtop -n 1 --memory
# htop: press M to sort by memory
htop
Real-time monitoring
bash
# Update every 2 seconds
watch -n 2 'free -h && echo "---" && ps aux --sort=-%rss | head -8'
04
Quick fixes
1. Free filesystem cache (safe, no data loss)
bash
sync && echo 3 > /proc/sys/vm/drop_caches
2. Restart services with memory leaks
bash
# MySQL/MariaDB
systemctl restart mysql
# PHP-FPM
systemctl restart php8.1-fpm
# Nginx
systemctl reload nginx
# Redis
systemctl restart redis
3. Add emergency swap
If you don't have swap configured:
bash
# Create a 2 GB swap file
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Make permanent
echo '/swapfile none swap sw 0 0' >> /etc/fstab
See the dedicated guide: Swap and swappiness.
05
Optimize MySQL for less RAM
MySQL is often the main consumer on small VPS.
bash
nano /etc/mysql/mysql.conf.d/mysqld.cnf
ini
[mysqld]
# For VPS with 2 GB of RAM
innodb_buffer_pool_size = 256M # default: 128M, don't overdo it
innodb_buffer_pool_instances = 1
key_buffer_size = 32M
max_connections = 50 # reduce if you don't have many connections
thread_cache_size = 8
query_cache_type = 0 # disable query cache (obsolete)
tmp_table_size = 32M
max_heap_table_size = 32M
bash
systemctl restart mysql
Optimal buffer pool calculation
bash
# Empirical rule: 70-80% of available RAM for dedicated databases
# On 2 GB VPS with only MySQL: ~1.2 GB
# On 2 GB VPS with web server + MySQL: ~512 MB
06
Optimize PHP-FPM
bash
nano /etc/php/8.1/fpm/pool.d/www.conf
ini
; Reduces number of active PHP processes
pm = dynamic
pm.max_children = 10 # default 5 per GB of available RAM
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500 # restart processes after N requests (prevents leaks)
bash
systemctl restart php8.1-fpm
07
Optimize Nginx
bash
nano /etc/nginx/nginx.conf
nginx
worker_processes auto; # one worker per CPU core
worker_connections 1024; # reduce if you have little RAM
# Disable access log (saves I/O and some RAM)
access_log off;
# Memory caching for static files
open_file_cache max=1000 inactive=20s;
08
Preventive monitoring
Alerting with bash script
bash
nano /usr/local/bin/check-ram.sh
bash
#!/bin/bash
THRESHOLD=90 # percentage
TOTAL=$(free | awk '/Mem:/{print $2}')
USED=$(free | awk '/Mem:/{print $3}')
PERCENT=$((USED * 100 / TOTAL))
if [ $PERCENT -gt $THRESHOLD ]; then
echo "ALERT: RAM at ${PERCENT}% on $(hostname)" | \
mail -s "[ALERT] High RAM" admin@tuodominio.com
fi
bash
chmod +x /usr/local/bin/check-ram.sh
# Run every 5 minutes
(crontab -l; echo "*/5 * * * * /usr/local/bin/check-ram.sh") | crontab -
For more complete monitoring with graphs and built-in alerting, see the guide Netdata.
Related articles
Common Issues
Locked Out of VPS
Complete guide to recover server access when locked out, with step-by-step instructions from VNC Console
5 min read
Common Issues
Server Unreachable
What to do when server is not responding or you can't connect via SSH
3 min read
Common Issues
Website Not Reachable
What to do when website is not responding, shows errors, or is unreachable
3 min read
