Configurar swap y evitar caídas por falta de memoria
Servicio caído, sin informe de fallo y con el log cortado a media frase: así demuestras un OOM-Kill, creas un archivo de swap correctamente y reconoces cuándo el swap solo aplaza el problema.
El servicio ha desaparecido. Sin informe de fallo, sin stacktrace, el archivo de log termina en mitad de una línea. MariaDB ya no responde, nginx devuelve 502, el servidor de Minecraft está offline y en la propia aplicación no aparece nada llamativo. Casi siempre es el mismo patrón: el kernel se quedó sin memoria libre y terminó un proceso para mantener el sistema con vida. Este artículo muestra cómo demostrarlo sin lugar a dudas, cómo crear un archivo de swap correctamente y dejarlo registrado de forma permanente, y en qué casos el swap solo aplaza el problema unos minutos.
Reunir la prueba: dmesg y journalctl
Antes de configurar nada necesitas la prueba. En cada OOM-Kill (Out of Memory) el kernel escribe un bloque detallado en el búfer circular:
dmesg -T | grep -iE 'out of memory|oom-kill'
Si no vuelve ninguna línea y el comando termina con código de salida 1, no ha habido ningún OOM del kernel desde el último arranque. Ese código de salida no es un error, es simplemente la respuesta habitual de grep cuando no encuentra coincidencias. Si devuelve algo, en los cuatro sistemas que tratamos aquí tiene este aspecto:
mariadbd invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/mariadb.service,task=mariadbd,pid=1043,uid=107
Out of memory: Killed process 1043 (mariadbd) total-vm:2894760kB, anon-rss:1583204kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:3820kB oom_score_adj:0
Aquí hay tres detalles importantes. Primero: invoked oom-killer nombra el proceso que hizo la petición de memoria, no forzosamente al culpable. Segundo: el proceso terminado aparece en la línea Killed process. Tercero: lo determinante es anon-rss, es decir, la memoria anónima realmente ocupada. total-vm es espacio de direcciones reservado y con Java o Go suele ser un múltiplo de aquel valor, así que no dice nada.
Si ejecutas dmesg sin root, en Debian 12, Debian 13, Ubuntu 22.04 y Ubuntu 24.04 responde con dmesg: read kernel buffer failed: Operation not permitted, porque kernel.dmesg_restrict está a 1 en todos ellos. Trabaja entonces con sudo o como root. Si el mensaje sigue apareciendo incluso como root, estás dentro de un contenedor LXC u OpenVZ que no tiene acceso al búfer circular del sistema anfitrión. En ese caso solo queda el segundo camino, a través de journalctl -k.
El búfer circular queda vacío tras un reinicio. De ahí el segundo camino, a través del journal, y aquí está la trampa que la mayoría de los tutoriales pasa por alto:
journalctl -k --grep "Out of memory"
-k implica -b, así que muestra únicamente el arranque actual. Si la máquina se reinició después del incidente, este comando no devuelve nada aunque el evento sí quedara registrado. Para el arranque anterior o para un periodo de tiempo concreto:
journalctl -k -b -1 --grep "Out of memory"
journalctl --since "7 days ago" --grep "Out of memory|oom-kill"
Si el primero de los dos comandos responde con No journal boot entry found for the specified boot (-1), es que simplemente no hay ningún arranque anterior guardado en el journal. Tampoco es un error, es lo normal en un sistema cuyo journal solo registra desde el arranque actual.
Esto solo funciona si el journal es realmente persistente. Compruébalo:
ls -d /var/log/journal
En Debian y Ubuntu este directorio viene de fábrica, así que la comprobación casi siempre da resultado y el mkdir siguiente es una operación en vacío que no rompe nada. Si por excepción el directorio no existe, el journal solo vive en /run y se pierde con cada reinicio. Se soluciona con dos comandos:
mkdir -p /var/log/journal
systemctl restart systemd-journald
¿OOM del kernel o límite de systemd? Dos causas distintas
De esta distinción depende que el swap sirva de algo. Fíjate en el campo constraint del mensaje del kernel.
CONSTRAINT_NONE significa que a todo el sistema se le acabó la memoria. Aquí el swap ayuda.
CONSTRAINT_MEMCG significa que solo un control group concreto superó su límite, mientras que el resto del sistema tenía margen de sobra. Aquí el swap no ayuda: hay que subir el límite o conseguir que la aplicación consuma menos. Este tipo de kills también se reconoce en el propio servicio:
systemctl status mariadb
Si ahí aparece Main process exited, code=killed, status=9/KILL y más abajo Failed with result 'oom-kill', fue un OOM-Kill. Si además hubo un límite de cgroup en juego, lo delata un archivo contador. Requiere cgroup v2, que es el estándar en las cuatro distribuciones; con el antiguo cgroup v1 la ruta no existe:
cat /sys/fs/cgroup/system.slice/mariadb.service/memory.events
systemctl show mariadb -p MemoryMax -p MemoryHigh
Un valor mayor que cero en oom_kill junto con un MemoryMax definido es la prueba de un límite local.
Hay un tercer candidato que se pasa por alto a menudo, porque no escribe absolutamente nada en dmesg: systemd-oomd. Este servicio trabaja en espacio de usuario, evalúa el indicador de presión PSI y termina control groups enteros antes de que el kernel llegue a intervenir. Su mensaje en el journal dice, en esencia, Killed /system.slice/... due to memory pressure for /system.slice being 60.00% > 50.00% for > 20s with reclaim activity. Comprueba si está en ejecución:
systemctl is-active systemd-oomd
En Ubuntu, systemd-oomd viene instalado y activo de fábrica desde la 22.04; en Debian no forma parte del equipamiento estándar. Un inactive en un servidor Debian es por tanto esperable y no indica ningún error.
Importante para más adelante: systemd-oomd también puede dispararse porque se llena el swap. Con oomd activo, más swap puede provocar las interrupciones incluso antes en lugar de después.
Antes del swap: cuánta memoria falta realmente
Dos minutos de medición te ahorran una decisión equivocada.
free -h
Lo único interesante es la columna available, no free. La caché cuenta como disponible y se libera cuando hace falta. Quien lee la columna free acaba tomando por sobrecargado cualquier sistema sano.
ps -eo pid,comm,rss,%mem --sort=-rss | head -n 11
systemd-cgtop --order=memory -b -n 1
El primer comando muestra los procesos individuales más grandes, el segundo los agrupa por servicios. En la práctica ahí aparecen casi siempre los mismos tres sospechosos: MariaDB con un innodb_buffer_pool_size elegido demasiado grande, PHP-FPM con un pm.max_children demasiado alto y una JVM con un -Xmx demasiado generoso.
Como tamaño de partida para el archivo de swap basta con esta tabla. En un servidor, más grande no es mejor, porque un swap que llegas a usar de verdad por completo deja la máquina inutilizable.
| RAM | Archivo de swap razonable |
|---|---|
| 1 GB | 1 a 2 GB |
| 2 GB | 2 GB |
| 4 a 8 GB | 2 a 4 GB |
| 16 GB o más | 4 GB, rara vez más |
Crear el archivo de swap
Primero conviene mirar si ya existe swap. Los servidores Ubuntu instalados desde la ISO suelen traer ya un /swap.img, y Debian desde su instalador crea normalmente una partición de swap real. Las imágenes cloud de ambas distribuciones no traen nada por regla general.
swapon --show
Si la salida queda vacía, no hay swap. A continuación comprueba el sistema de archivos, porque de él depende el procedimiento:
findmnt -no FSTYPE -T /
Con ext4 o xfs se puede continuar directamente. Crea el archivo con dd, no con fallocate. fallocate es más rápido, pero según el sistema de archivos y el kernel genera un archivo con zonas no escritas, y entonces swapon lo rechaza. dd escribe ceros reales y funciona en todas partes:
dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
chmod 600 /swapfile
Antes del siguiente paso conviene hacer una comprobación que mucha gente se salta. Si bajo /swapfile ya hay un swap activado, por ejemplo de un intento anterior o de la configuración por defecto de la imagen, mkswap se niega a trabajar con mkswap: error: /swapfile is mounted; will not make swapspace. Lo único determinante es si la ruta figura como activa en /proc/swaps. Así que revísalo y desactívalo si hace falta:
swapon --show
swapoff /swapfile
Si en swapon --show no aparece ninguna línea con /swapfile, puedes saltarte el swapoff. Después ya se escribe el área de intercambio:
mkswap /swapfile
mkswap responde con Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) y un UUID nuevo. Solo después se activa:
swapon /swapfile
El orden no es negociable: chmod antes de mkswap, mkswap antes de swapon, y un eventual swapoff antes que todo lo demás.
Cómo saber que realmente ha funcionado
Que swapon termine sin mensaje de error todavía no es una prueba. La prueba son estas dos salidas:
swapon --show
free -h
swapon --show tiene que mostrar una línea con /swapfile, file, el tamaño y una prioridad. En free -h, la línea Swap: tiene que haber saltado de 0B al nuevo tamaño. Si alguna de las dos salidas sigue igual, el swap no está activo, diga lo que diga el comando anterior.
Dejarlo permanente sin poner en riesgo el arranque
Un swapon no sobrevive a un reinicio. La entrada va en /etc/fstab, y es justo ahí donde se destrozan servidores. Primero, la copia de seguridad:
cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Fíjate en los dos signos de mayor que. Un solo > sobrescribe el archivo entero y entonces la máquina ya no arranca correctamente. Después comprueba la sintaxis, antes de reiniciar:
findmnt --verify
Un aviso concreto es completamente normal y no es motivo para volver a quitar la línea: [W] non-bind mount source /swapfile is a directory or regular file. Con un archivo de swap eso es inevitable y aun así el comando termina con valor de retorno 0. Si al archivo le falta además una cabecera de swap válida, se suma [W] cannot detect on-disk filesystem type, y entonces sí que se te ha olvidado un mkswap.
Si más arriba ya activaste el swap a mano con swapon /swapfile, ahora ya está activo. Si no, swapon -a activa todas las entradas del fstab sin que tengas que reiniciar. Pero la prueba de verdad es otra. systemd genera una unit propia a partir de cada línea del fstab; para /swapfile se llama swapfile.swap. Si esa unit aparece y está activa, el swap se montará con toda seguridad en el siguiente arranque:
systemctl daemon-reload
systemctl list-units --type swap
Se espera una línea swapfile.swap loaded active active Swap. Si falta, la línea del fstab no es correcta y un reinicio acabaría sin swap. Solo cuando esto encaja merece la pena reiniciar y comprobarlo después con swapon --show.
Ajustar swappiness correctamente
El parámetro del kernel vm.swappiness controla con qué facilidad se envían páginas anónimas al swap en lugar de descartar caché de archivos. El valor por defecto es idéntico en Debian 12, Debian 13, Ubuntu 22.04 y Ubuntu 24.04: 60.
cat /proc/sys/vm/swappiness
Desde el kernel 5.8 el rango de valores va de 0 a 200, y las cuatro distribuciones están por encima de esa versión. Dos errores muy extendidos: vm.swappiness=0 no desactiva el swap, solo evita el intercambio preventivo, y aun así el kernel recurre al swap antes de matar un proceso. Y un valor bajo no hace más rápido a un sistema al que sencillamente le falta memoria.
Valores razonables: de 10 a 20 en servidores de bases de datos, 60 en servidores web mixtos, 100 o más si usas zram. De forma permanente esto va en un archivo propio bajo /etc/sysctl.d/, no en /etc/sysctl.conf, que estorba en las actualizaciones de paquetes:
echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swappiness.conf
sysctl --system
cat /proc/sys/vm/swappiness
El tercer comando es la comprobación del resultado. sysctl --system lee todos los directorios en un orden fijo, y un archivo ya existente con un número más alto puede sobrescribir tu valor.
Cuando algo sale mal: los mensajes de error, literales
swapon: /swapfile: insecure permissions 0644, 0600 suggested. Es solo un aviso, el swap funciona igualmente. Aun así conviene corregirlo, porque si no cualquier usuario puede leer el contenido de los procesos enviados al swap: chmod 600 /swapfile.
swapon: /swapfile: swapon failed: Invalid argument El error más frecuente. O se olvidó el mkswap, o el archivo contiene huecos. En el log del kernel aparece además swapon: swapfile has holes. Solución: borrar el archivo y volver a crearlo con dd en lugar de fallocate.
En btrfs aparece el mismo error más BTRFS warning: swapfile must not be copy-on-write. Aquí el orden es otro: el archivo hay que crearlo vacío y marcarlo como no copy-on-write antes de rellenarlo. Un apunte previo que puede decidir el destino de un servidor en producción: truncate -s 0 y rm se ejecutan sin más aunque bajo esa ruta siga montado un swap activo. Después de eso el kernel apunta a bloques que ya no existen. Por eso, si swapon --show muestra la ruta como activa, delante va obligatoriamente un swapoff /swapfile.
truncate -s 0 /swapfile
chattr +C /swapfile
Después se rellena como siempre con dd, chmod 600, mkswap, swapon. En subvolúmenes comprimidos y dentro de snapshots el swap sigue sin funcionar.
swapon: /swapfile: swapon failed: Operation not permitted Estás dentro de un contenedor. LXC, OpenVZ y Docker comparten el kernel del anfitrión y no pueden activar un swap propio. Compruébalo con:
systemd-detect-virt
Si el comando devuelve kvm, qemu o none, hay un kernel propio y el swap es posible. Si devuelve lxc, openvz o docker, solo ayuda más RAM o un producto con virtualización completa. Los servidores root KVM y los servidores dedicados de KernelHost traen su propio kernel, así que ahí el swap se puede configurar sin restricciones.
dd: error writing '/swapfile': No space left on device El disco está demasiado lleno. Primero df -h, después elimina con rm /swapfile el archivo escrito a medias, porque si no sigue ocupando espacio. Aquí vale lo mismo: si swapon --show muestra la ruta como activa, antes del borrado va un swapoff /swapfile.
El sistema ya no arranca y aparece la consola de emergencia. Casi siempre es una errata en /etc/fstab. Inicia sesión a través de la consola del área de cliente, luego mount -o remount,rw /, quita la línea defectuosa o vuelve a copiar /etc/fstab.bak, y reinicia. Justo para esto se creó antes la copia de seguridad.
Diferencias entre Debian 13, Debian 12, Ubuntu 24.04 y 22.04
Los comandos son idénticos en los cuatro sistemas, el estado de partida no.
- Swap ya existente: Ubuntu Server instalado desde la ISO suele crear
/swap.img, y Debian desde su instalador una partición de swap. Las imágenes cloud de ambas distribuciones vienen sin swap. Siempre primeroswapon --show. - systemd-oomd: en Ubuntu está instalado y activo de fábrica desde la 22.04, en ambas versiones de Debian no forma parte del equipamiento estándar. Eso explica por qué un software idéntico puede morir de forma distinta en dos sistemas aparentemente iguales.
- Base de datos: Debian 12 y Debian 13 no incluyen
mysql-server, allí siempre corre MariaDB (10.11 en Debian 12, 11.8 en Debian 13). Ubuntu 22.04 y 24.04 tienen ambas. Los valores por defecto deinnodb_buffer_pool_sizedifieren en consecuencia, y precisamente ese valor es la causa de OOM más frecuente en servidores pequeños. - Java: Debian 12 solo conoce OpenJDK 17, Debian 13 solo OpenJDK 21, y Ubuntu 22.04 y 24.04 cubren de la 8 a la 21. Una JVM sin
-Xmxdefinido se queda por defecto con una cuarta parte de la RAM, así que con varias instancias el OOM-Kill está cantado. - Mensajes del kernel: el formato y la redacción del mensaje de OOM son iguales en los cuatro sistemas, los ejemplos de arriba valen en todos.
- swappiness: 60 por defecto en todos.
Cuándo ayuda el swap y cuándo solo aplaza el problema
El swap ayuda de forma fiable con picos cortos, por ejemplo durante un backup, una actualización de paquetes o una importación nocturna. Ayuda con servicios que ocupan mucha memoria y después pasan días sin hacer nada, porque esas páginas pueden quedarse tranquilamente en el disco. Y en caso de emergencia te da unos minutos en los que la sesión SSH todavía responde y puedes intervenir, en lugar de quedarte delante de un servidor muerto.
El swap no ayuda cuando la necesidad permanente supera sin más a la RAM disponible. Entonces el sistema empieza a hacer thrashing: intercambia sin parar hacia fuera y hacia dentro, la load sube a valores de dos cifras, el uso de CPU se mantiene bajo y todo espera a la I/O. En la práctica eso es peor que un OOM-Kill limpio, porque ni siquiera el inicio de sesión llega a entrar. Dos comandos muestran si estás en ese estado:
vmstat 1 5
test -e /proc/pressure/memory && cat /proc/pressure/memory
En vmstat lo que cuenta son las columnas si y so. Valores de tres cifras de forma sostenida significan intercambio activo en ambos sentidos. En /proc/pressure/memory lo decisivo es full avg10: valores por encima de 10 significan que el sistema entero pasa el diez por ciento del tiempo esperando memoria. Por encima de 40 la máquina está prácticamente muerta. Ahora bien, el archivo solo existe si Pressure Stall Information está activo en el kernel. Los kernels estándar de Debian y Ubuntu lo traen, otros kernels no forzosamente. De ahí la protección con test -e: si el archivo falta, la salida queda vacía en lugar de que tomes un No such file or directory por una avería. PSI se puede añadir después mediante el parámetro de arranque del kernel psi=1.
Qué procesos están realmente en el swap lo responde esta línea:
grep VmSwap /proc/*/status | sort -k2 -rn | head
El swap tampoco sirve de nada frente a un límite de cgroup (CONSTRAINT_MEMCG), frente a memoria fijada con mlock ni frente a una JVM cuyo heap está configurado más grande que la RAM disponible. El recolector de basura recorre periódicamente todo el heap y devuelve de inmediato cada página que estaba en el swap.
Las tres herramientas para cuando el swap no basta
zram crea un área de intercambio comprimida dentro de la propia RAM. Es órdenes de magnitud más rápida que un archivo en el disco y, según los datos, aporta entre un 20 y un 40 por ciento más de memoria efectiva:
apt update
apt install -y zram-tools
La configuración se hace en /etc/default/zramswap mediante ALGO=zstd y PERCENT=50, y después systemctl restart zramswap. Para comprobarlo, zramctl y de nuevo swapon --show, donde entonces aparece /dev/zram0. Con zram, vm.swappiness debe subir a entre 100 y 180, porque aquí el intercambio sale barato.
earlyoom interviene antes de que el kernel congele el sistema y mata de forma dirigida en lugar de por heurística:
apt install -y earlyoom
En /etc/default/earlyoom defines los umbrales mediante EARLYOOM_ARGS y proteges los procesos importantes, por ejemplo con -m 5 -s 5 --avoid '(^|/)(sshd|systemd)$'. Así el inicio de sesión por SSH sigue accesible mientras cae el proceso que devora la memoria.
Los límites fijos por servicio son la solución más limpia cuando un servicio concreto se desmadra con regularidad. Con systemctl edit mariadb añades MemoryHigh=1200M y MemoryMax=1500M. El servicio se frena entonces y, si hace falta, se termina él solo en lugar de arrastrar consigo al servidor entero. Comprobación con systemctl show mariadb -p MemoryMax.
Aun así, en la mayoría de los casos el arreglo de verdad sigue siendo la configuración de la aplicación: innodb_buffer_pool_size en una medida realista, pm.max_children según el consumo real de memoria por worker, y un -Xmx definido para cada JVM. El swap es la red de seguridad, no la solución.
Marcha atrás, si no encaja
El camino de vuelta es corto y debería seguir este orden:
swapoff /swapfile
Con un swap muy ocupado el comando puede tardar varios minutos, porque todas las páginas tienen que volver a la RAM. Si termina en swapoff: /swapfile: swapoff failed: Cannot allocate memory, es que en la RAM no hay sitio para el traslado de vuelta y primero hay que parar servicios. Solo después de un swapoff correcto se quita la línea del fstab y se borra el archivo:
rm /swapfile
swapon --show
Borrar el archivo mientras sigue montado deja un sistema cuya gestión de memoria apunta a un inodo que ya no existe. Eso acaba mal. Para terminar, otra vez free -h y un reinicio como contraprueba.
Preguntas frecuentes
¿Cuánto swap necesito en un servidor?
¿Por qué journalctl -k no muestra ningún OOM-Kill aunque lo hubo?
¿Cómo reconozco si fue el kernel o un límite de systemd el que terminó el proceso?
¿Por qué falla swapon con "Invalid argument"?
¿Puedo configurar swap dentro de un contenedor?
¿vm.swappiness=0 significa que ya no se intercambia nada?
2026 KernelHost GmbH. Todos los derechos reservados. Esta guía está protegida por derechos de autor. Su publicación en otros sitios web, aunque sea de forma parcial o modificada, no está permitida sin nuestro consentimiento por escrito. Las citas con indicación de la fuente y un enlace son muy bienvenidas.

