Skip to content

Fix: Linux OOM Killer Killing Processes (Out of Memory)

FixDevs ·

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/0x2f0

Or 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 signal

Why 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.log

Interpret 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:10000kB

Monitor 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 -20

Fix 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/fstab

Tune 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.conf

Note: 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_adj

Set 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 attempted
sudo systemctl daemon-reload
sudo systemctl restart myapp

Set 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.procs

Docker 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 myimage

Fix 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.conf

Real-world scenario: A Redis server running with vm.overcommit_memory=0 uses 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 setting vm.overcommit_memory=1 to 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
done

Use valgrind for C/C++ memory leak detection:

valgrind --leak-check=full --track-origins=yes ./myapp

For 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 minute

For 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 use

Enable 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 OOM

Review application memory configuration:

  • JVM: Set -Xmx to limit the heap. Without it, the JVM can grow to consume all available RAM.
  • Node.js: Use --max-old-space-size=4096 to limit heap to 4GB.
  • PostgreSQL: Review shared_buffers, work_mem, and max_connections.
  • Redis: Set maxmemory and maxmemory-policy in redis.conf.

For related Linux issues, see Fix: Linux Disk Full — No Space Left on Device and Fix: Linux Too Many Open Files.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles