systemd-oomd y Docker limits: lo que nadie te cuenta para proteger tu sistema
TL;DR: Habilita systemd-oomd — protege tu escritorio matando procesos selectivamente cuando la memoria falla. Si usas Docker, pon límites de memoria a tus containers — sin límite, un container con fuga puede comerse toda la RAM del host.
Todo lo anterior — zram, sysctl, swap por capas — da más margen. Pero si un proceso crece 10 GB por una fuga de memoria, solo hay dos cosas que pueden salvar tu sesión: un OOM killer inteligente y containers con límites.
Este artículo tiene dos partes independientes:
- systemd-oomd — para todos, uses o no Docker
- Docker limits — solo si usas Docker
systemd-oomd: mata el proceso, no el escritorio
El OOM killer del kernel no es inteligente. Mata lo que más pese, no lo que más convenga. Y si el proceso culpable corre dentro del cgroup de gnome-shell, systemd marca todo el servicio como fallido — gnome-shell muere con él.
systemd-oomd es el userspace OOM killer de systemd. Entiende cgroups y puede matar procesos selectivamente.
Configuración
# Crear configuración
sudo mkdir -p /etc/systemd/oomd.conf.d
sudo tee /etc/systemd/oomd.conf.d/10-defaults.conf << 'EOF'
[OOM]
SwapUsedLimit=90%
DefaultMemoryPressureLimit=60%
DefaultMemoryPressureDurationSec=30s
EOF
# Habilitar
sudo systemctl enable --now systemd-oomd
Qué hace:
- Si el swap supera 90% de uso, mata procesos para liberar memoria
- Si un cgroup tiene más del 60% de presión de memoria durante 30 segundos, mata procesos dentro de ese cgroup (no el padre)
- Si tu IDE se descontrola dentro del cgroup de GNOME, mata la IDE, no el escritorio
Verificar
systemctl status systemd-oomd
# Active: active (running)
Limitación
systemd-oomd no previene el OOM del kernel. Si zram se satura globalmente, el kernel invoca su propio OOM killer antes de que systemd-oomd reaccione. Por eso necesitas las otras capas (zram, sysctl, swap disco).
Docker limits: containers sin límite son una bomba
¿No usas Docker? Puedes saltarte esta sección. Lo anterior (systemd-oomd) ya te protege.
Si usas Docker, probablemente tus containers no tienen límite de memoria:
docker inspect --format '{{.Name}} | Memory={{.HostConfig.Memory}}' $(docker ps -q)
Si todos dicen Memory=0, no hay límite. Un container con fuga de memoria puede comerse toda la RAM del host sin que nada lo pare.
En docker-compose.yml
services:
postgres:
image: postgres:16-alpine
deploy:
resources:
limits:
memory: 2G
Nota sobre memory-swap: define el total máximo de RAM + swap que el container puede usar:
# Container puede usar 2G RAM + 2G swap = 4G total
deploy:
resources:
limits:
memory: 2G
memory-swap: 4G
# Container limitado a 2G total, sin acceso a swap
deploy:
resources:
limits:
memory: 2G
memory-swap: 2G
Para containers ya corriendo
Sin recrear el container, puedes aplicar límites temporalmente:
docker update --memory 2g --memory-swap 2g nombre-del-container
Ojo: esto se pierde si el container se recrea. Para hacerlo persistente, actualiza el compose file.
Qué pasa cuando un container supera el límite
Docker mata el proceso dentro del container con OOM kill. El host no se ve afectado. El container aparece como OOMKilled en docker ps -a, y puedes reiniciarlo.
Guía rápida de límites por tipo de servicio
| Tipo de servicio | Límite recomendado | Ejemplos |
|---|---|---|
| Base de datos | 2-4 GB | postgres, mysql |
| Message broker | 256-512 MB | nats, redis |
| API / Backend | 512 MB - 1 GB | gotrue, postgrest |
| Frontend / Studio | 1 GB | supabase-studio |
| Utilidad | 128-256 MB | adminer, inbucket |
La regla: empieza con 2-3x el uso normal y ajusta:
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}"
Monitorización: comandos útiles
# Estado de zram
zramctl
# Uso de swap
swapon --show
# Memoria total
free -h
# systemd-oomd
systemctl status systemd-oomd
# Sysctl
sysctl vm.swappiness vm.page-cluster vm.watermark_scale_factor
# OOM scores de procesos
ps -eo pid,comm,rss:10,oom_score:8,oom_score_adj:12 --sort=-rss | head -20
Signos de alarma
zramctlmuestra DATA cercano a DISKSIZE → zram casi llenoFree swapen los logs del kernel es menor a 1 GB → poco margendmesg | grep -i oommuestra actividad reciente → el sistema sigue bajo presión
Qué NO hacer
No instalar earlyoom Y systemd-oomd a la vez
Ambos son OOM killers en userspace. Si están activos los dos, pueden competir por matar el mismo proceso y causar condiciones de carrera. Usa solo systemd-oomd.
No cambiar OOMPolicy en gnome-shell
La tentación es cambiar OOMPolicy=stop a OOMPolicy=continue. Pero gnome-shell espera que sus procesos hijos funcionen. Si un hijo muere a medio render, el estado puede quedar inconsistente. Mejor prevenir con systemd-oomd.
No confiar solo en sysctl --system
Una regla udev de tu distribución puede reescribir swappiness cada vez que zram cambia de estado. Siempre verifica con cat /proc/sys/vm/swappiness después de cambios.
Artículos de esta serie
- Cómo duplicar la RAM de tu Linux con zram y swap comprimida (sin comprar nada)
- zram: mitad de RAM, algoritmo zstd y por qué tu distro lo tiene mal configurado
- Swappiness 180: el valor que hace funcionar zram (y la regla udev que te lo estaba impidiendo)
- RAM → zram → swap en disco: cómo montar un sistema de swap por capas y probarlo con stress-ng
- systemd-oomd y Docker limits: lo que nadie te cuenta para proteger tu sistema ← estás aquí
Referencias
- systemd-oomd(8) — documentación oficial
- Kernel docs — OOM killer
- CachyOS Forum — Zram + systemd-oomd disabled — exactamente el mismo caso que el nuestro