Memory Pressure, PSI and the OOM Killer: Deciding What Dies
Running out of memory on Linux rarely produces a clean error. It produces a system that gets slow, then unresponsive, and then kills something, frequently the wrong thing.
Why the wrong process dies
The kernel OOM killer picks a victim by score, and the score is driven heavily by how much memory the process uses.
That heuristic is reasonable on a server full of similar workers. It is wrong on almost any other machine, because size is not the same as expendability.
A desktop shell is large: it holds the compositor, the window manager and the UI. Killing it destroys your session. The browser tab that actually consumed the memory may be a smaller individual process and survive.
# current score for a process, higher means killed sooner
cat /proc/$(pidof gnome-shell)/oom_score
# the adjustment applied to it, -1000 to 1000
cat /proc/$(pidof gnome-shell)/oom_score_adj
# what the kernel killed last time
journalctl -k | grep -i 'out of memory\|oom-kill' | tail
That journalctl line is the first thing to run when a machine mysteriously lost a process.
Pressure stall information
PSI answers a better question than utilisation does. Memory being 95% used tells you very little. Tasks spending 40% of their time stalled waiting for memory tells you the system is in trouble.
cat /proc/pressure/memory
cat /proc/pressure/io
cat /proc/pressure/cpu
some avg10=12.45 avg60=8.21 avg300=3.10 total=48219348
full avg10=4.12 avg60=2.88 avg300=1.02 total=15884201
some means at least one task was stalled. full means every runnable task was stalled, so nothing useful happened at all.
full above zero for any sustained period is the signal that matters. It means the machine is doing nothing except waiting, which is the state users describe as frozen.
PSI is also available per cgroup, which is how you find out which workload is causing it:
cat /sys/fs/cgroup/system.slice/myservice.service/memory.pressure
Setting scores that reflect importance
# /etc/systemd/system/important.service.d/override.conf
[Service]
OOMScoreAdjust=-500
ManagedOOMPreference=avoid
# something you would rather lose
[Service]
OOMScoreAdjust=500
The range is -1000 to 1000. At -1000 a process is effectively exempt, which is a dangerous setting: if the exempt process is the one leaking, the kernel will kill everything else and then panic.
Our systemd service guide covers where overrides live and how to apply them without editing packaged units.
The two mechanisms that disagree
This is the part that wastes people’s time.
The kernel OOM killer acts on OOM scores when memory is genuinely exhausted.
systemd-oomd acts on pressure stall information and cgroup usage, before the kernel would, and does not read the kernel’s OOM scores.
So carefully setting OOMScoreAdjust=-500 on your desktop shell buys you nothing against systemd-oomd, which may decide the user session is under pressure and terminate something critical regardless.
systemctl status systemd-oomd
oomctl
Ubuntu concluded this was doing more harm than good on the desktop and disabled the session-wide memory-pressure kill in 26.10, leaving the kernel killer, which does honour scores, as the mechanism of last resort. That is one policy instead of two competing ones.
The tradeoff is real: systemd-oomd exists because the kernel acts late. Removing the pressure-based kill can mean more time spent thrashing before anything happens.
earlyoom and the case for acting sooner
The kernel’s threshold is genuinely out of memory, which on a desktop arrives long after the machine became unusable. You spend several minutes unable to move the mouse, and then something dies.
earlyoom and similar daemons kill something while the system is still responsive.
sudo apt install earlyoom
sudo systemctl enable --now earlyoom
# /etc/default/earlyoom
EARLYOOM_ARGS="-r 60 -m 5 -s 5 --avoid '(^|/)(gnome-shell|Xorg|sshd)$'"
Losing a browser at 5% free memory is better than losing twenty minutes of interactivity and then losing the browser anyway.
On a server the calculation differs. A thrashing server may still be serving requests slowly, and killing the database to restore responsiveness may be worse than being slow.
Swap, and what it actually buys
Swap does not prevent running out of memory. It changes the shape of the failure.
Without swap, the system fails quickly and obviously. With swap, it may thrash for a long time first, which frequently feels worse.
Compressed swap in RAM is usually the better answer. Compressing a page and keeping it in memory costs microseconds; writing it to disk and faulting it back costs far more. Our zram guide covers the setup, and swap space explained covers the sizing question including hibernation.
# is zswap active
cat /sys/module/zswap/parameters/enabled
swapon --show
free -h
GNOME OS now enables zswap by default specifically to reduce how often machines reach the OOM condition at all, which is the right order of operations: make the crisis rarer first, then decide who dies when it happens anyway.
Bounding the damage before it is system-wide
The cleanest fix is to stop one workload from being able to exhaust the machine.
[Service]
MemoryMax=2G
MemoryHigh=1500M
MemoryHigh applies throttling pressure; MemoryMax is a hard ceiling that triggers a cgroup-local OOM kill. The process dies, and the rest of the system never notices.
Our cgroups guide covers this in depth, and for containers the equivalent is --memory, discussed in container hardening.
This is the approach worth reaching for first. Tuning who the OOM killer picks is managing a crisis. Limiting each workload is preventing one.
A diagnostic sequence
# 1. is there real pressure, or just high usage
cat /proc/pressure/memory
# 2. who is using it
ps aux --sort=-%mem | head -10
systemd-cgtop -m
# 3. what died and when
journalctl -k --since '1 hour ago' | grep -i oom
# 4. which cgroup is generating the pressure
for f in /sys/fs/cgroup/*/memory.pressure; do echo "== $f"; cat "$f"; done
Step one matters most. High memory usage with no pressure is Linux doing its job: unused RAM is wasted RAM, and the page cache will give memory back on demand. Our memory monitoring guide covers why free -h alarms people unnecessarily.
Frequently Asked Questions
Why does the OOM killer terminate the wrong process?
Because it scores candidates largely by how much memory they use, and size is not the same as expendability. A desktop shell or a database is large and important, while the process that actually caused the pressure may be smaller. The scoring needs adjusting to reflect what you can afford to lose.
What is pressure stall information?
PSI measures how much time tasks spend stalled waiting for a resource, reported in /proc/pressure for CPU, memory and I/O. Unlike a utilisation percentage it tells you whether contention is actually hurting anything, which is the question you usually want answered.
Why does systemd-oomd kill things the kernel would not?
They use different criteria. systemd-oomd acts on pressure stall information and cgroup memory usage, while the kernel OOM killer acts on its own scores. A process protected by a low OOM score is not protected from systemd-oomd, because systemd-oomd is not reading that score.
How do I protect a specific service from being killed?
Set OOMScoreAdjust to a negative value in its systemd unit, which lowers its kernel OOM score. For systemd-oomd, set ManagedOOMPreference to avoid. You need both if both mechanisms are active, because neither honours the other settings.
Does adding swap prevent out of memory conditions?
It delays them rather than preventing them, and the delay can be unpleasant. Without swap a system fails quickly and obviously. With swap it may thrash for a long time first, which feels worse than a clean failure. Compressed swap in RAM is usually the better compromise.
What is the earlyoom approach and when is it better?
earlyoom and similar userspace daemons act before the kernel does, killing something while the system is still responsive rather than after it has already become unusable. That is often preferable on desktops, where the kernel threshold is late enough that you have lost interactivity well before anything is killed.