The Linux Out-Of-Memory (OOM) killer terminates processes when the kernel cannot satisfy memory allocations under the applicable memory constraints; the decisive log signatures include Out of memory: Killed process, oom-kill, Memory cgroup out of memory, and Killed process <PID> (<name>), while the underlying cause may be exhausted physical RAM, insufficient swap, a memory-hungry process, kernel memory pressure, or a cgroup/systemd/container memory limit. The OOM killer is a kernel-level recovery mechanism rather than an application error, and the correct fix is to identify which memory domain was exhausted before changing vm.* parameters or simply increasing RAM.
What the Linux OOM Killer Actually Does
Linux normally attempts to reclaim memory before invoking the OOM killer. Reclaim can include filesystem cache, anonymous memory management, swap activity, and other reclaimable pages. When the kernel cannot satisfy an allocation within the relevant memory policy or cgroup, an OOM condition can occur.
A typical kernel message looks like:
Out of memory: Killed process 18472 (java) total-vm:...
Other useful signatures include:
oom-kill:constraint=CONSTRAINT_NONE
Memory cgroup out of memory: Killed process 1234 (node)
Killed process 1234 (postgres)
The distinction between a system-wide OOM and a cgroup OOM is critical. A server can have apparently available RAM while a container, systemd service, virtual machine, or Kubernetes workload has already reached its assigned memory limit.
Stage 1: Log Inspection and Diagnostic Verification
Step 1: Find the Exact OOM Event
Start with the kernel journal:
sudo journalctl -k --since "24 hours ago" --no-pager | grep -Ei 'out of memory|oom-kill|killed process|memory cgroup'
On systems using traditional kernel logs:
sudo grep -Ei 'out of memory|oom-kill|killed process|memory cgroup' /var/log/syslog /var/log/messages 2>/dev/null
Check the current boot:
sudo journalctl -k -b --no-pager | grep -Ei 'out of memory|oom-kill|killed process|memory cgroup'
For the complete event surrounding an OOM kill:
sudo journalctl -k -b --no-pager | less
Do not focus only on the final Killed process line. The preceding kernel messages often reveal the memory state, cgroup, allocation type, and available swap at the time of failure.
Step 2: Check Current RAM and Swap
Run:
free -h
A more detailed view is:
cat /proc/meminfo
Pay particular attention to:
grep -E 'MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree|SwapCached|SReclaimable|Shmem' /proc/meminfo
MemAvailable is generally more useful for estimating how much memory can be allocated without swapping than MemFree, because Linux deliberately uses otherwise unused RAM for caches.
Check swap devices:
swapon --show
Then:
cat /proc/swaps
If:
SwapTotal: 0 kB
appears in /proc/meminfo, the server has no configured swap space. That does not automatically mean swap must be added, but it removes an important buffer during periods of memory pressure.
Step 3: Identify the Process Consuming Memory
Use ps to rank processes by resident memory:
ps -eo pid,ppid,user,%mem,rss,vsz,comm --sort=-rss | head -n 25
For a continuously updating view:
top
Inside top, press:
Shift+M
to sort by memory consumption.
If htop is installed:
htop
The RSS value is especially useful because it represents resident memory currently held in physical RAM. VSZ is virtual address space and should not be interpreted as equivalent to actual RAM consumption.
For a specific process:
PID=1234
sudo cat /proc/$PID/status | grep -E 'VmPeak|VmSize|VmRSS|VmSwap|RssAnon|RssFile|RssShmem'
Step 4: Determine Whether Memory Usage Is Growing
A process that uses 10 GB consistently is different from one that starts at 500 MB and continuously grows until the kernel kills it.
Record process memory periodically:
while true; do
date
ps -p 1234 -o pid,etime,%mem,rss,vsz,cmd
sleep 10
done
For multiple processes:
watch -n 5 'ps -eo pid,%mem,rss,vsz,comm --sort=-rss | head -n 15'
A continuously increasing RSS value can indicate a memory leak, an unbounded cache, excessive concurrency, unexpectedly large workloads, or an application configuration problem.
Step 5: Check Pressure Stall Information
Linux provides memory pressure information through PSI:
cat /proc/pressure/memory
You may see:
some avg10=0.00 avg60=0.00 avg300=0.00 total=...
full avg10=0.00 avg60=0.00 avg300=0.00 total=...
High memory-pressure values indicate that processes are spending significant time stalled because of memory contention. PSI is particularly useful when the server is suffering severe memory pressure before an OOM event occurs.
Stage 2: Core Configuration Fix and Service Recovery
Step 6: Determine Which Process the Kernel Actually Killed
Search the kernel journal for the PID:
sudo journalctl -k --since "24 hours ago" --no-pager | grep -Ei 'Killed process|oom-kill'
You may see:
Killed process 18472 (java) total-vm:...
Find information about that process if it still exists:
ps -p 18472 -o pid,ppid,user,%mem,rss,vsz,etime,cmd
If it has already exited:
sudo journalctl -k -b --no-pager | grep '18472'
The process killed by OOM is not necessarily the original cause of the memory problem. Linux chooses a process based on its OOM scoring and memory characteristics, so the terminated process may simply have been the process selected to release enough memory.
Step 7: Inspect the OOM Score
For a running process:
PID=1234
cat /proc/$PID/oom_score
cat /proc/$PID/oom_score_adj
The kernel’s OOM selection mechanism considers the process’s OOM-related score. A process with a modified oom_score_adj can therefore be treated differently from another process with the same approximate memory consumption.
Inspect important services:
for pid in $(pgrep -x sshd); do
echo "PID=$pid"
cat /proc/$pid/oom_score_adj
done
Do not automatically set critical applications to oom_score_adj=-1000. Making an essential service effectively immune to OOM termination can force the kernel to kill other processes or leave the machine in a prolonged memory-starved state.
Step 8: Check systemd Memory Limits
A service can encounter an OOM condition because systemd has placed it inside a memory-controlled cgroup.
Inspect the service:
sudo systemctl show nginx -p MemoryMax -p MemoryHigh -p MemoryCurrent -p MemorySwapMax
For another service:
sudo systemctl show myapp.service -p MemoryMax -p MemoryHigh -p MemoryCurrent -p MemorySwapMax
Check the complete cgroup information:
sudo systemctl status myapp.service
Also inspect:
systemctl show myapp.service | grep -Ei '^Memory(Max|High|Current|SwapMax)='
A service with:
MemoryMax=2G
can be killed by the memory controller even if the host still has unused RAM.
Step 9: Check the Cgroup Memory Controller
Determine the process’s cgroup:
PID=1234
cat /proc/$PID/cgroup
On cgroup v2 systems:
mount | grep cgroup
Inspect the memory controller:
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.high
A value of:
max
for memory.max means there is no explicit maximum at that cgroup level.
For a systemd service, use:
systemctl status myapp.service
systemctl show myapp.service -p ControlGroup
Then inspect the corresponding cgroup path under /sys/fs/cgroup/.
This distinction matters because increasing server RAM will not necessarily solve a cgroup memory limit that is deliberately set below the host’s physical capacity.
Step 10: Reduce Application Memory Consumption
If a specific service repeatedly consumes excessive memory, correct its application-level configuration before changing kernel behavior.
For a systemd-managed application:
sudo systemctl edit myapp.service
A service-level memory limit can be explicitly defined:
[Service]
MemoryHigh=6G
MemoryMax=8G
Then:
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
MemoryHigh and MemoryMax have different purposes. A high threshold can apply memory-pressure control before the hard maximum is reached, while MemoryMax provides a hard cgroup memory boundary.
Do not blindly add limits to production applications. A limit that is lower than legitimate working-set requirements can convert ordinary workload growth into repeated service termination.
Step 11: Configure Swap When Appropriate
Check existing swap:
swapon --show
If no swap exists and the workload would benefit from additional virtual memory, create a swap file.
First verify available filesystem capacity:
df -h /
Create a file:
sudo fallocate -l 4G /swapfile
Secure it:
sudo chmod 600 /swapfile
Initialize it:
sudo mkswap /swapfile
Enable it:
sudo swapon /swapfile
Verify:
swapon --show
free -h
Make it persistent:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Then validate:
sudo mount -a
Do not assume a large swap file fixes the underlying problem. Swap can provide additional virtual-memory capacity, but sustained swapping can severely degrade application performance.
Stage 3: Root Cause Prevention and Kernel/System Limit Tuning
Step 12: Inspect vm.swappiness
Check the current setting:
sysctl vm.swappiness
The setting influences the kernel’s tendency to reclaim anonymous memory through swap relative to other reclaimable memory.
For a temporary test:
sudo sysctl vm.swappiness=10
To make a deliberate configuration persistent:
sudo tee /etc/sysctl.d/90-memory.conf >/dev/null <<'EOF'
vm.swappiness = 10
EOF
Apply:
sudo sysctl --system
The appropriate value depends on the workload, kernel behavior, available RAM, storage performance, and whether swap is intentionally used. There is no universally correct production value.
Step 13: Inspect Commit-Limit Configuration
Check:
sysctl vm.overcommit_memory
sysctl vm.overcommit_ratio
grep -E 'CommitLimit|Committed_AS' /proc/meminfo
Linux memory overcommit controls influence whether memory allocations are permitted beyond immediately available physical memory and swap.
Do not change vm.overcommit_memory simply because an OOM event occurred. An inappropriate setting can alter application allocation behavior and create different failure modes.
If the application explicitly requires a particular overcommit policy, configure it through:
sudo tee /etc/sysctl.d/91-vm-overcommit.conf >/dev/null <<'EOF'
vm.overcommit_memory = 1
EOF
Then:
sudo sysctl --system
Only apply this when the workload’s documented memory-management requirements justify it.
Step 14: Check File Descriptor and Process Limits
Memory incidents can sometimes be accompanied by excessive process or descriptor creation.
Check:
ulimit -a
For a service:
sudo systemctl show myapp.service -p LimitNOFILE -p TasksMax
Check the process count:
ps -e --no-headers | wc -l
Check the kernel PID limit:
cat /proc/sys/kernel/pid_max
For a systemd service:
sudo systemctl show myapp.service -p TasksCurrent -p TasksMax
A runaway process-spawning workload can consume memory through large numbers of processes or threads even when no individual process looks exceptionally large.
Step 15: Investigate Kernel Memory Consumption
If user-space processes do not account for the missing memory, inspect slab usage:
sudo slabtop
Or:
grep -E 'Slab|SReclaimable|SUnreclaim' /proc/meminfo
Inspect complete memory information:
cat /proc/meminfo
A large SUnreclaim value can indicate significant unreclaimable kernel memory. Drivers, networking workloads, filesystem activity, and kernel subsystems can contribute to kernel memory pressure.
Do not attempt to solve unexplained kernel memory consumption by randomly changing cache or slab-related sysctl values. Identify the subsystem responsible first.
Step 16: Monitor Memory Pressure Before the Next OOM Event
A simple monitoring loop is:
while true; do
date
free -h
echo "-----"
cat /proc/pressure/memory
echo "-----"
ps -eo pid,%mem,rss,comm --sort=-rss | head -n 10
sleep 30
done
For a production environment, collect memory utilization, swap activity, PSI, process RSS, cgroup memory usage, OOM events, and application-specific metrics.
The important objective is to identify whether memory usage rises gradually, spikes during a particular workload, or reaches a fixed cgroup limit.
Diagnosing Common OOM Scenarios
Java Applications
Check the Java process:
pgrep -af java
Then:
PID=$(pgrep -xo java)
grep -E 'VmRSS|VmSize|VmSwap' /proc/$PID/status
For JVM configuration:
jcmd $PID VM.flags
If jcmd is available, inspect heap information:
jcmd $PID GC.heap_info
Do not set the Java heap equal to all available server RAM. The JVM also requires native memory, thread stacks, class metadata, direct buffers, and other allocations outside the Java heap.
MySQL or MariaDB
Check:
sudo systemctl show mysql -p MemoryCurrent -p MemoryMax
or:
sudo systemctl show mariadb -p MemoryCurrent -p MemoryMax
Then inspect the process:
ps -C mysqld -o pid,%mem,rss,vsz,cmd
Database buffer pools and connection counts can create substantial memory demand. Correcting database configuration is generally preferable to forcing the kernel to tolerate an undersized host.
Nginx, Apache, and PHP-FPM
For PHP-FPM:
ps -C php-fpm8.2 -o pid,%mem,rss,vsz,cmd
The exact executable name varies by distribution and PHP version:
pgrep -af 'php-fpm'
Inspect pool configuration:
sudo find /etc/php -type f -path '*/fpm/pool.d/*.conf' -print
Common worker controls include:
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
The correct pm.max_children depends on the application’s actual per-worker memory usage. Increasing it without measuring worker RSS can turn a traffic spike into an OOM event.
Containers and Kubernetes
Check Docker memory limits:
docker stats
For a specific container:
docker inspect CONTAINER_ID --format '{{.HostConfig.Memory}}'
For Kubernetes:
kubectl top pods -A
Inspect configured resources:
kubectl get pod POD_NAME -n NAMESPACE -o yaml
Look for:
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi"
A container terminated with an OOM-related status may have exceeded its configured cgroup memory limit rather than exhausting the entire host.
What Not to Change During an OOM Incident
Do Not Disable the OOM Killer
The OOM killer exists because the kernel needs a recovery mechanism when memory allocation cannot be satisfied. Disabling or attempting to defeat it without solving memory pressure can leave the server unresponsive.
Do Not Arbitrarily Increase Kernel Memory Values
Changing:
vm.swappiness
vm.overcommit_memory
vm.overcommit_ratio
without understanding the workload can hide the actual problem or create another one. First establish whether the pressure originates from user-space memory, swap exhaustion, a cgroup limit, kernel memory, or excessive process creation.
Do Not Automatically Protect Critical Processes
Changing:
echo -1000 | sudo tee /proc/PID/oom_score_adj
can make the selected process harder for the kernel to kill, but it does not create additional memory. The kernel may instead terminate another important service.
If process protection is required, document why that process must survive and verify that sufficient memory capacity exists for the remaining workload.
Final Verification After the Repair
Step 17: Confirm Memory Capacity
Run:
free -h
Then:
swapon --show
Then:
grep -E 'MemAvailable|SwapFree|CommitLimit|Committed_AS' /proc/meminfo
Step 18: Confirm the Previously Affected Service
sudo systemctl status myapp.service --no-pager
Check recent failures:
sudo journalctl -u myapp.service -b --no-pager | tail -n 100
Check its current memory usage:
sudo systemctl show myapp.service -p MemoryCurrent -p MemoryHigh -p MemoryMax
Step 19: Confirm That New OOM Events Are Not Occurring
sudo journalctl -k --since "1 hour ago" --no-pager | grep -Ei 'out of memory|oom-kill|killed process|memory cgroup'
If this returns nothing after the repair and the workload has passed through the period that previously triggered OOM, continue monitoring rather than assuming the problem is permanently resolved.
Final Diagnostic Sequence
For a production Linux server experiencing repeated OOM kills, run the following sequence before changing kernel parameters:
sudo journalctl -k --since "24 hours ago" --no-pager | grep -Ei 'out of memory|oom-kill|killed process|memory cgroup'
free -h
swapon --show
ps -eo pid,ppid,user,%mem,rss,vsz,comm --sort=-rss | head -n 25
cat /proc/meminfo
cat /proc/pressure/memory
systemctl --failed
sudo systemctl show SERVICE_NAME -p MemoryCurrent -p MemoryHigh -p MemoryMax -p MemorySwapMax
PID=$(pgrep -xo PROCESS_NAME)
sudo cat /proc/$PID/status | grep -E 'VmRSS|VmSwap|RssAnon|RssFile|RssShmem'
sysctl vm.swappiness
sysctl vm.overcommit_memory
grep -E 'Slab|SReclaimable|SUnreclaim' /proc/meminfo
The correct resolution should follow the evidence from these checks. If physical RAM and swap are genuinely exhausted, reduce application memory consumption, increase available memory, or add appropriately sized swap. If only a service cgroup is exhausted, adjust its workload or memory limit. If a single process grows continuously, investigate its configuration or memory leak. If kernel memory dominates, investigate the relevant kernel subsystem or driver. If the system has severe memory pressure without a clear user-space culprit, use /proc/meminfo, PSI, cgroup statistics, and historical monitoring to establish where the memory is being consumed before making kernel-level changes.