Fix: Linux OOM Killer Killing Processes (Out of Memory)
Quick Answer
How to fix Linux OOM killer terminating processes — reading oom_kill logs, adjusting oom_score_adj, adding swap, tuning vm.overcommit, and preventing memory leaks.
The Error
A process is killed unexpectedly and you find this in the kernel logs:
Mar 19 03:42:11 server kernel: Out of memory: Killed process 12345 (node) total-vm:2048000kB, anon-rss:1800000kB, file-rss:0kB, shmem-rss:0kB, UID:1000 phandle:0x3f7
Mar 19 03:42:11 server kernel: oom_kill_process+0x2c8/0x2f0Or an application exits with no error but the system log shows:
kernel: oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user.slice,task=node,pid=12345,uid=1000
kernel: Out of memory: Killed process 12345 (node)Or a container is terminated with exit code 137:
Container exited with code 137
# Exit code 137 = 128 + 9 (SIGKILL), which is the OOM killer's signalWhy This Happens
Linux allocates memory optimistically — it allows processes to request more memory than physically available, assuming not all of it will be used at once (overcommit). When actual physical memory runs out and the kernel can’t free enough through page cache eviction, the OOM (Out of Memory) killer activates. It selects a process to kill based on a score (oom_score) that weighs memory usage, process priority, and other factors.
Common causes:
- Memory leak — a process’s memory usage grows unbounded until the system runs out.
- Insufficient RAM for the workload — the total memory demand of all processes exceeds physical RAM plus swap.
- No swap space — without swap, the kernel has no overflow buffer. A single large memory spike can trigger the OOM killer.
- cgroup memory limit — in containers or cgroup-constrained environments, the OOM killer activates when the process exceeds the cgroup limit, even if system memory is available.
- Memory fragmentation — the system has free memory but not in large enough contiguous blocks (rare, but causes OOM-like behavior).
vm.overcommit_memory=2— disables overcommit. Allocations fail when total committed memory exceeds physical RAM + swap, even before memory is exhausted.
Fix 1: Identify What the OOM Killer Killed
Start by confirming an OOM kill occurred and identifying the victim:
# Check kernel logs for OOM events
sudo dmesg | grep -i "oom\|killed process\|out of memory"
# Or check system journal (systemd systems)
sudo journalctl -k | grep -i oom
# Or check syslog
sudo grep -i oom /var/log/syslog
sudo grep -i oom /var/log/kern.logInterpret the OOM log:
Out of memory: Killed process 12345 (node) total-vm:2048000kB, anon-rss:1800000kB
# ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# PID Name Memory usage at time of kill
#
# total-vm: Total virtual memory (may be much larger than RAM used)
# anon-rss: Actual RAM used by anonymous mappings (heap, stack) — this is the key number
# file-rss: RAM used for file-backed pages (libraries, mmap files)Check memory state at time of kill:
# OOM log includes system memory state — look for lines like:
# [ 1234.567] Mem-Info:
# [ 1234.567] Node 0 DMA free:15360kB min:256kB low:320kB high:384kB
# [ 1234.567] active_anon:450000kB inactive_anon:200000kB active_file:10000kBMonitor memory usage in real time to find leaking processes:
# Sort by memory usage
watch -n 2 "ps aux --sort=-%mem | head -20"
# Or use top — press M to sort by memory
top
# smem gives more accurate real memory usage (PSS)
smem -t -k -c "pid name pss" | sort -k 3 -n | tail -20Fix 2: Add or Increase Swap Space
Swap gives the kernel an overflow buffer and significantly reduces OOM kills for temporary memory spikes:
# Check current swap
free -h
swapon --show
# Create a 4GB swap file
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Verify swap is active
free -h
# Swap: 4.0G
# Make swap permanent across reboots
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabTune swappiness — controls how aggressively the kernel uses swap:
# Check current swappiness (default is 60)
cat /proc/sys/vm/swappiness
# Lower values prefer RAM; higher values prefer using swap
# For servers with large RAM, set lower (10-20)
sudo sysctl vm.swappiness=10
# Make permanent
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-memory.conf
sudo sysctl -p /etc/sysctl.d/99-memory.confNote: Swap prevents OOM kills but trades memory for disk I/O. If a process is actively swapping, performance degrades significantly. Swap buys time — the real fix is reducing memory usage or adding RAM.
Fix 3: Adjust OOM Score to Protect Critical Processes
The OOM killer selects its victim using oom_score (0–1000). Higher scores are killed first. You can adjust a process’s score using oom_score_adj (-1000 to 1000):
# Check current OOM score for a process
cat /proc/$(pgrep nginx)/oom_score
# e.g., 150
# Check the adjustment value
cat /proc/$(pgrep nginx)/oom_score_adj
# 0 (default)Make a critical process less likely to be killed:
# Lower the OOM score for nginx (less likely to be killed)
echo -500 | sudo tee /proc/$(pgrep nginx)/oom_score_adj
# Make a process nearly immune to OOM kill
echo -1000 | sudo tee /proc/$(pgrep nginx)/oom_score_adj
# Make a process the first target (useful for sacrificial processes)
echo 1000 | sudo tee /proc/$(pgrep nginx)/oom_score_adjSet oom_score_adj for a systemd service permanently:
# /etc/systemd/system/myapp.service
[Service]
OOMScoreAdjust=-500 # Lower score = less likely to be killed
# Or disable OOM kill entirely for this service (use carefully)
OOMPolicy=continue # Continue running even if OOM kill is attemptedsudo systemctl daemon-reload
sudo systemctl restart myappSet oom_score_adj when starting a process:
# Start a process with a specific OOM score adjustment
sudo -u myuser bash -c 'echo $$ > /tmp/myapp.pid && exec nice -n 10 /usr/bin/myapp'
# Or use the oom_score_adj directly with a wrapper
sudo sh -c 'echo 500 > /proc/self/oom_score_adj && exec sudo -u myuser /usr/bin/background-job'Fix 4: Set Memory Limits with cgroups
Instead of letting the OOM killer react, proactively limit how much memory a process can use:
Using systemd service limits:
# /etc/systemd/system/myapp.service
[Service]
MemoryMax=2G # Hard limit — process is OOM-killed if it exceeds this
MemoryHigh=1.5G # Soft limit — triggers memory reclaim before hard limit
MemorySwapMax=0 # Disable swap for this service (0 = no swap allowed)Using cgroups v2 directly:
# Create a cgroup
sudo mkdir /sys/fs/cgroup/myapp
# Set memory limit to 2GB
echo "2147483648" | sudo tee /sys/fs/cgroup/myapp/memory.max
# Add a process to the cgroup
echo <PID> | sudo tee /sys/fs/cgroup/myapp/cgroup.procsDocker container memory limits:
# Limit container to 512MB RAM and 512MB swap
docker run --memory=512m --memory-swap=1g myimage
# Prevent container from using swap
docker run --memory=512m --memory-swap=512m myimageFix 5: Tune vm.overcommit Settings
Linux’s memory overcommit behavior controls whether malloc() can succeed when physical memory isn’t available:
# Check current setting
cat /proc/sys/vm/overcommit_memory
# 0 = heuristic overcommit (default)
# 1 = always allow overcommit (never fail malloc)
# 2 = strict — fail when committed memory > (RAM * overcommit_ratio/100 + swap)vm.overcommit_memory=0 (default): The kernel guesses whether to allow allocation. Can lead to OOM kills when guesses are wrong.
vm.overcommit_memory=1: Always allow malloc() to succeed. Maximum memory efficiency but maximum OOM risk.
vm.overcommit_memory=2: Strict — fail malloc() early instead of OOM-killing later. Use when you’d rather have processes fail predictably than have random OOM kills:
# Set strict overcommit
sudo sysctl vm.overcommit_memory=2
sudo sysctl vm.overcommit_ratio=80 # Allow RAM * 80% + swap as committed memory
# Make permanent
echo 'vm.overcommit_memory=2' | sudo tee -a /etc/sysctl.d/99-memory.conf
echo 'vm.overcommit_ratio=80' | sudo tee -a /etc/sysctl.d/99-memory.confReal-world scenario: A Redis server running with
vm.overcommit_memory=0uses fork-based snapshotting (BGSAVE). Fork briefly doubles memory usage. If the system is tight on memory, the fork triggers the OOM killer. Redis’s own documentation recommends settingvm.overcommit_memory=1to prevent this.
Fix 6: Find and Fix Memory Leaks
If the OOM killer repeatedly targets the same process, that process likely has a memory leak:
Monitor memory growth over time:
# Watch a specific process's memory usage every 30 seconds
PID=$(pgrep myapp)
while true; do
RSS=$(awk '/VmRSS/{print $2}' /proc/$PID/status)
echo "$(date): RSS=${RSS}kB"
sleep 30
doneUse valgrind for C/C++ memory leak detection:
valgrind --leak-check=full --track-origins=yes ./myappFor Node.js — use the built-in heap profiler:
// Add to your Node.js application
const v8 = require('v8');
const fs = require('fs');
// Dump heap snapshot (analyze with Chrome DevTools)
setInterval(() => {
const snapshot = v8.writeHeapSnapshot();
console.log(`Heap snapshot written to ${snapshot}`);
}, 60000); // Every minuteFor Python — use tracemalloc:
import tracemalloc
tracemalloc.start()
# ... run your code ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)Set up automatic memory monitoring with alerting:
# Simple script — alert when any process exceeds 80% of total RAM
THRESHOLD=$(($(grep MemTotal /proc/meminfo | awk '{print $2}') * 80 / 100))
ps aux --sort=-%mem | awk -v threshold=$THRESHOLD 'NR>1 {
rss = $6 # RSS in kB
if (rss > threshold) {
print "WARNING: Process " $11 " (PID " $2 ") using " rss "kB"
}
}'Still Not Working?
Check if the OOM kill is from a cgroup, not the global OOM killer:
# cgroup OOM kills show different messages
sudo dmesg | grep "Memory cgroup"
# Memory cgroup out of memory: Kill process 12345 (myapp)...For cgroup OOM kills, increase the cgroup memory limit or reduce the process’s memory usage — adding system swap won’t help.
Check for huge pages fragmentation:
# If the system uses huge pages and fragmentation prevents allocation
cat /proc/meminfo | grep -i huge
# HugePages_Total: 100
# HugePages_Free: 0 ← All huge pages are in useEnable panic on OOM instead of killing (for debugging only):
# Don't use in production — causes a kernel panic instead of OOM kill
echo 1 | sudo tee /proc/sys/vm/panic_on_oom
# 0 = OOM kill (default)
# 1 = panic if OOM kill fails
# 2 = always panic on OOMReview application memory configuration:
- JVM: Set
-Xmxto limit the heap. Without it, the JVM can grow to consume all available RAM. - Node.js: Use
--max-old-space-size=4096to limit heap to 4GB. - PostgreSQL: Review
shared_buffers,work_mem, andmax_connections. - Redis: Set
maxmemoryandmaxmemory-policyinredis.conf.
For related Linux issues, see Fix: Linux Disk Full — No Space Left on Device and Fix: Linux Too Many Open Files.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AWS ECS Task Failed to Start
How to fix ECS tasks that fail to start — port binding errors, missing IAM permissions, Secrets Manager access, essential container exit codes, and health check failures.
Fix: Docker Multi-Stage Build COPY --from Failed
How to fix Docker multi-stage build errors — COPY --from stage not found, wrong stage name, artifacts not at expected path, and BuildKit caching issues.
Fix: AWS CloudWatch Logs Not Appearing
How to fix AWS CloudWatch logs not showing up — IAM permissions missing, log group not created, log stream issues, CloudWatch agent misconfiguration, and Lambda log delivery delays.
Fix: Certbot Certificate Renewal Failed (Let's Encrypt)
How to fix Certbot certificate renewal failures — domain validation errors, port 80 blocked, nginx config issues, permissions, and automating renewals with systemd or cron.