VPS Memory Optimization: Reduce RAM Usage, Configure Swap, and Fix OOM Kills
The Linux OOM (Out of Memory) killer is one of the most disruptive VPS events — when RAM is exhausted, the kernel kills processes to free memory, often taking down your web server, database, or application without warning. Memory pressure builds gradually: small leaks accumulate, traffic spikes fill RAM, or a new service is added without adjusting existing process limits. This guide diagnoses memory usage, configures swap as a safety valve, and tunes service configurations to reduce the memory footprint.
Step 1: Diagnose Current Memory Usage
# Overview: how much RAM is used, free, and available
free -h
# Sample output showing a problem — no swap!
# total used free shared buff/cache available
# Mem: 1.9G 1.7G 82M 45M 180M 185M
# Swap: 0B 0B 0B <-- No swap! Dangerous.
# Top 15 processes by memory consumption
ps aux --sort=-%mem | head -16
# Memory usage per process (RSS = actual RAM used)
ps -eo pid,comm,rss --sort=-rss | awk 'NR<=15 {printf "%6d MB %s\n", $3/1024, $2}'
# Check if OOM killer has fired recently
sudo dmesg | grep -i "oom\|killed process" | tail -20
sudo journalctl -k | grep -i "oom\|out of memory" | tail -20
Step 2: Add Swap Space (Critical for Small VPS)
Swap gives the kernel a safety valve — instead of OOM-killing processes, it moves inactive memory pages to disk. On NVMe SSDs, swap performs at 500–1000 MB/s — fast enough to prevent crashes for short bursts without noticeably impacting performance.
# Create a 2 GB swap file (recommended: 2× RAM for VPS with ≤2 GB)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Verify swap is active
free -h # Swap row should now show 2.0G
# Make permanent across reboots
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Step 3: Tune vm.swappiness
vm.swappiness controls how aggressively the kernel moves pages to swap. Default is 60 (quite aggressive). For a VPS where you want to use RAM fully but avoid OOM kills:
# View current value
cat /proc/sys/vm/swappiness # Usually 60
# Set to 10: prefer RAM but swap when genuinely tight
sudo sysctl vm.swappiness=10
# Make permanent
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
vm.swappiness=10: Use RAM first, swap only when tight — good for web serversvm.swappiness=1: Swap only as absolute last resort — good for database serversvm.swappiness=60: Default — too aggressive for most VPS workloads
Step 4: Reduce PHP-FPM Memory Usage
PHP-FPM’s process pool is the most common RAM overconsumption source. Each PHP-FPM worker uses 30–80 MB depending on WordPress plugins loaded.
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
# For a 1 GB VPS:
pm = dynamic
pm.max_children = 5 # Total PHP processes maximum
pm.start_servers = 2 # Processes at startup
pm.min_spare_servers = 1 # Minimum idle processes
pm.max_spare_servers = 3 # Maximum idle processes
pm.max_requests = 500 # Restart each worker after 500 requests (clears memory leaks)
# For a 2 GB VPS:
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
# For a 4 GB VPS:
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
sudo systemctl restart php8.2-fpm
# Monitor PHP-FPM memory usage
watch -n5 'ps aux --sort=-%mem | grep php-fpm | head -10'
Step 5: Right-Size MariaDB/MySQL Buffer Pool
MariaDB’s innodb_buffer_pool_size often defaults to or gets misconfigured at 1 GB+ — too large for small VPS instances.
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
# For 1 GB VPS:
innodb_buffer_pool_size = 64M
# For 2 GB VPS:
innodb_buffer_pool_size = 128M
# For 4 GB VPS:
innodb_buffer_pool_size = 512M
# General memory savings
query_cache_type = 0 # Disable query cache (causes contention, wastes RAM)
tmp_table_size = 32M
max_heap_table_size = 32M
max_connections = 50 # Reduce from default 151
sudo systemctl restart mariadb
Step 6: Reduce Nginx Worker Memory
sudo nano /etc/nginx/nginx.conf
worker_processes auto; # Match CPU count (auto is correct)
events {
worker_connections 512; # Reduce from 1024 for low-RAM VPS
}
http {
keepalive_timeout 30; # Reduce from 65 — frees connections faster
keepalive_requests 100; # Limit persistent connection reuse
}
Step 7: Protect Critical Processes from OOM Killer
The kernel assigns OOM scores (higher = more likely to be killed). Protect critical services:
# Add to systemd service files to protect SSH from OOM kills:
sudo nano /etc/systemd/system/ssh.service.d/oom.conf
[Service]
OOMScoreAdjust=-1000 # -1000 = never kill this process
# Also protect Nginx:
sudo mkdir -p /etc/systemd/system/nginx.service.d/
sudo nano /etc/systemd/system/nginx.service.d/oom.conf
[Service]
OOMScoreAdjust=-500
sudo systemctl daemon-reload
Memory Usage Targets by VPS Size
| Total RAM | Swap Size | Stack Recommendation |
|---|---|---|
| 1 GB | 2 GB | Nginx + PHP-FPM (3 workers) + MariaDB (64 MB) + Redis (32 MB) |
| 2 GB | 2 GB | Nginx + PHP-FPM (8 workers) + MariaDB (128 MB) + Redis (64 MB) |
| 4 GB | 2 GB | Nginx + PHP-FPM (15 workers) + MariaDB (512 MB) + Redis (128 MB) + Docker |
| 8 GB | 4 GB | Full stack: monitoring, multiple apps, databases, Docker containers |
Getting Started
Memory optimization is most critical on smaller Ubuntu VPS plans. The swap configuration and PHP-FPM tuning here keep a 1–2 GB VPS stable under normal traffic. When memory optimization is exhausted and the VPS still runs hot, upgrading to the next RAM tier is the right solution — VPS.DO allows plan upgrades without migrating to a new server.
Conclusion
VPS memory management follows a clear priority: add swap as a safety valve (prevents OOM kills during temporary spikes), reduce PHP-FPM process counts to match available RAM, right-size the MariaDB buffer pool, and protect critical services from OOM score. A 1 GB VPS can reliably run WordPress with proper PHP-FPM tuning and 2 GB swap. The OOM killer is the symptom — the configurations above address the root causes.