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.

DeluxHost, opgericht in 2023, biedt hoogwaardige hostingoplossingen voor diverse digitale behoeften. Wij bieden gedeelde hosting, VPS en dedicated servers met geavanceerde beveiliging en wereldwijde datacenters.

© DeluxHost, Alle rechten voorbehouden. | BTW-nummer: IT17734661006
Alle systemen operationeel