Introduction
Recently, I needed a way to force the Linux OOM killer to terminate a specific process. The goal was to test certain systemd failover behavior, because it turns out that systemd handles an OOM kill (Result: oom-kill) differently than a process being stopped using kill (Result: signal).
Since I could not readily find sources on how to do this – most resources discuss how to trigger the OOM killer in general, but not target a specific process – I decided to write up what I ended up doing, both as a reference for myself and in case it proves useful to someone else.
Mechanism
The most reliable and controlled method to trigger the OOM killer for a process is to use control groups (cgroups). If the target process has its own dedicated cgroup, writing a very low value (or zero) to memory.max will cause the next memory allocation to exceed the limit, triggering the OOM killer.
This requires that your process/service does not share its cgroup, otherwise you will also cause other processes to be terminated.
The kill occurs on allocation. An idle process such as sleep may survive until it attempts to allocate memory.
The control file memory.max is specific to cgroup v2, which is used by the majority of recent Linux distributions. For cgroup v1 the control file is called memory.limit_in_bytes and the hierarchy is slightly different. The rest of this post will assume cgroup v2.
systemd services
When the target process is running as a systemd service, these cgroups are already in place. For a service foo.service with no custom resource slices, it can be done like this:
echo 0 > /sys/fs/cgroup/system.slice/foo.service/memory.maxThe command above assumes you are running it as root. If not, you can use sudo like this:
echo 0 | sudo tee /sys/fs/cgroup/system.slice/foo.service/memory.maxThis command assumes you know which specific cgroup you need, which is fine for one-off usage. If you need something more robust you can retrieve the ControlGroup property of the service, which will have a value like /system.slice/foo.service. This also works with nested cgroups and resources slices. Example:
echo 0 > "/sys/fs/cgroup$(systemctl show --property=ControlGroup --value foo.service)/memory.max"Avoid using systemctl set-property because this will persist the low memory limit and will prevent the service from starting again.
Process without a cgroup
You might have a long-running process that you want to OOM kill, but the process lacks a dedicated cgroup. This can be solved by creating one for it, as demonstrated by the following snippet (requires root):
# Substitute this with the actual process ID
TARGET_PID=1234
# Create the cgroup
CGROUP=/sys/fs/cgroup/oom-kill
mkdir "$CGROUP"
# Move the process into the cgroup
echo "$TARGET_PID" > "$CGROUP/cgroup.procs"
# Lower the memory limit to trigger OOM kill
echo 0 > "$CGROUP/memory.max"
# Wait for process to be killed and cleaned up
sleep 1
# Clean up the cgroup
rmdir "$CGROUP"You can verify that this works by checking journalctl or dmesg to see that the Linux OOM killer intervened.
Conclusion
Forcing an OOM kill is useful for testing failover behavior or debugging memory constraints. This method using cgroups is an effective way to target a specific process and avoid unintended side effects.