The Linux Out-Of-Memory (OOM) killer is a last-resort mechanism that terminates processes when the system runs out of memory. By the time it activates, your services are already degraded — swap is exhausted, page reclaim has failed, and the system may be completely unresponsive. The OOM killer’s heuristic process selection often terminates the wrong service, taking down your database while leaving memory-leaking background processes alive.
Early OOM prevention daemons solve this by monitoring memory pressure before the kernel OOM killer activates. They detect memory exhaustion trends and gracefully terminate the least-critical processes before the system becomes unresponsive.
This guide compares three self-hosted OOM prevention approaches: earlyoom (standalone daemon), systemd-oomd (systemd-integrated), and PSI-based monitoring (Pressure Stall Information). We’ll cover deployment, configuration, and Docker Compose setups for container environments.
Why Early OOM Prevention Matters
The default Linux OOM killer has several problems for production servers:
- Reactive, not proactive — It only triggers when memory is already exhausted, often after the system has been swapping for minutes
- Heuristic victim selection — The OOM score calculation doesn’t understand your service priorities. It might kill PostgreSQL while keeping a memory-leaking cron job alive
- System unresponsiveness — By the time OOM killer activates, the system may be too slow to respond to SSH or health checks, triggering cascading failures
- No graceful shutdown — OOM killer sends SIGKILL (unblockable), giving processes no chance to save state or close connections
Early OOM prevention daemons monitor memory availability trends and act before the system reaches critical levels. They can:
- Send SIGTERM first (graceful shutdown), then SIGKILL if needed
- Target specific processes based on your configuration
- Alert your monitoring system before killing anything
- Use PSI metrics to detect memory pressure before actual exhaustion
For broader system reliability, see our container runtime security comparison and container seccomp profile management guide.
Comparison: earlyoom vs systemd-oomd vs PSI Monitors
| Feature | earlyoom | systemd-oomd | PSI-based monitors |
|---|---|---|---|
| Trigger mechanism | Available memory % | Memory pressure (PSI) | PSI (Pressure Stall Information) |
| Kill signal | SIGTERM then SIGKILL | SIGTERM (cgroup-level) | Configurable |
| Victim selection | Highest RSS process | cgroup with highest pressure | Configurable |
| Configuration | CLI flags + systemd unit | Drop-in config file | Custom scripts / exporters |
| cgroup v2 support | Yes | Yes (native) | Yes (native) |
| Container awareness | Basic (can exclude by name) | Full (cgroup-aware) | Full (cgroup-aware) |
| Notification support | Email, webhook, syslog | Journal only | Prometheus, alertmanager |
| Package availability | Most distros | systemd 247+ | Kernel 4.20+ (PSI) |
| Resource usage | ~2 MB RAM | ~3 MB RAM | Varies (depends on implementation) |
| Best for | Simple servers, desktops | systemd-based servers, containers | Advanced monitoring, K8s |
earlyoom: Simple Standalone OOM Prevention
earlyoom is a lightweight daemon that monitors available memory and swap, killing the largest memory consumer when levels drop below configured thresholds.
Installation
| |
Configuration
| |
| Flag | Description |
|---|---|
-m 5 | Trigger when available memory drops below 5% |
-s 10 | Trigger when swap drops below 10% |
-r 60 | Report memory status every 60 seconds |
--prefer | Regex for processes to prefer killing |
--avoid | Regex for processes to avoid killing |
Docker Compose Deployment
For container hosts running Docker Compose, earlyoom runs as a privileged container with access to the host’s /proc:
| |
Monitoring earlyoom
earlyoom logs to syslog by default. Check for OOM prevention events:
| |
For Prometheus monitoring, pair earlyoom with the node_exporter textfile collector to expose earlyoom metrics.
systemd-oomd: Integrated Memory Pressure Management
systemd-oomd is built into systemd (v247+) and uses PSI (Pressure Stall Information) metrics to detect memory pressure. Unlike earlyoom’s percentage-based triggers, systemd-oomd responds to actual memory stall times — the time processes spend waiting for memory allocation.
How PSI Works
PSI metrics (available since Linux 4.20) measure how long processes are stalled waiting for resources:
| |
some: Time when some tasks are stalled on memory allocationfull: Time when ALL non-idle tasks are stalled (system-wide memory pressure)avg10/60/300: Average stall percentage over 10/60/300 secondstotal: Cumulative stall time in microseconds
systemd-oomd monitors the full metric — when all tasks are stalled, the system is effectively unresponsive.
Configuration
| |
cgroup-Level OOM Control
systemd-oomd works at the cgroup level, making it ideal for container environments:
| |
Docker Compose with systemd-oomd Awareness
Configure your containers to work with systemd-oomd’s cgroup-level killing:
| |
Enabling systemd-oomd
| |
PSI-Based Custom Monitors
For advanced use cases, you can build custom OOM prevention monitors using PSI metrics directly from /proc/pressure/.
Simple PSI Monitor Script
| |
PSI Exporter for Prometheus
For Kubernetes and monitoring-centric environments, run a PSI exporter:
| |
Prometheus alert rule for PSI memory pressure:
| |
Container-Specific OOM Prevention
When running containers, OOM prevention needs to work at the container level, not just the host level.
Docker OOM Score Adjustments
Set OOM score adjustments per container to influence which containers get killed first:
| |
Kubernetes Memory Limits and OOM
In Kubernetes, set memory requests and limits to ensure the scheduler places pods correctly and the kubelet manages OOM:
| |
Choosing the Right OOM Prevention Strategy
For simple Linux servers: earlyoom is the easiest to deploy and configure. It works on any Linux distribution, requires minimal configuration, and handles the most common scenario: preventing system-wide lockups from runaway processes.
For systemd-based infrastructure: systemd-oomd is the most integrated option. It uses PSI metrics for accurate pressure detection, works at the cgroup level (perfect for containers), and requires no additional packages on modern systemd distributions.
For Kubernetes and monitoring-centric environments: PSI-based custom monitors give you the most flexibility. Export pressure metrics to Prometheus, set up alerting rules, and integrate with your existing incident response workflows. This approach is best when you need fine-grained control over which processes get killed and when.
FAQ
What is the difference between earlyoom and the kernel OOM killer?
The kernel OOM killer is a reactive mechanism that activates only when the system has zero available memory and swap is exhausted. It sends SIGKILL (unblockable) to the process with the highest OOM score. earlyoom is a proactive userspace daemon that monitors available memory percentages and kills processes before the system reaches zero. It sends SIGTERM first (allowing graceful shutdown), then SIGKILL if the process doesn’t exit within a timeout.
Does systemd-oomd work without PSI support?
No. systemd-oomd requires kernel PSI (Pressure Stall Information) support, which was introduced in Linux 4.20. Most modern distributions (Ubuntu 20.04+, RHEL 9+, Debian 11+) ship with kernels that support PSI. Verify with: cat /proc/pressure/memory. If this file doesn’t exist, your kernel doesn’t support PSI.
Can earlyoom and systemd-oomd run together?
It is not recommended. Both daemons monitor memory and may try to kill the same processes, creating race conditions. Choose one: use systemd-oomd on systems with systemd 247+ and PSI support, or earlyoom on older systems or non-systemd distributions.
How do I prevent OOM killer from terminating my database?
Three approaches:
- OOM score adjustment: Set
oom_score_adj=-1000(viasystemdunit or container config) to make the process immune to the OOM killer. Note: this also prevents earlyoom from killing it unless explicitly configured. - Memory limits: Set strict
mem_limitin Docker orresources.limits.memoryin Kubernetes so the container is killed before affecting the host. - earlyoom –avoid flag: Configure earlyoom to avoid processes matching specific patterns:
--avoid '(postgres|mysql|mongod)'.
What PSI threshold should I use for production servers?
The full metric’s avg10 (10-second average) is the most responsive indicator. A threshold of 30-60% means that for 10 seconds, 30-60% of non-idle time is spent stalled on memory allocation. Start with 60% and lower it if you experience system unresponsiveness before the OOM prevention triggers. Monitor the metric over a week to understand your server’s normal pressure patterns before setting thresholds.
Can OOM prevention daemons work inside containers?
Yes, but with limitations. The daemon needs access to /proc/pressure/memory for PSI metrics (requires CAP_SYS_ADMIN or running on a kernel with unprivileged PSI access). earlyoom can run inside a container with --privileged or by mounting /proc from the host. For Kubernetes, it is more effective to run OOM prevention at the node level (as a DaemonSet) rather than inside individual pods.