Debian 13: La swap crece y el rendimiento se desploma — Soluciones de presión de memoria que sí ayudan

¿Te fue útil?

Inicias sesión y todo se siente… pegajoso. SSH tarda un segundo de más. Un apt “rápido” se arrastra. Tus paneles muestran que la CPU está bien,
el disco no parece saturado y, sin embargo, la latencia sube como si compitiera en una carrera de subir escaleras.
Entonces lo notas: el uso de swap está creciendo. Al principio despacio, luego de forma implacable. La máquina no está caída, pero tampoco está viva.

En Debian 13, esto normalmente no significa “el swap es malo” tanto como “tu sistema está negociando con la física”. La buena noticia: puedes diagnosticar la presión de memoria
rápidamente, escoger la intervención correcta y dejar de tratar el swap como una superstición. La mala noticia: algunas “tuneadas” comunes lo empeoran.
Hagamos las correcciones que aguantan en producción.

Guía de diagnóstico rápido (revisa esto primero)

Cuando el swap sube y el rendimiento cae, quieres responder tres preguntas rápido:
(1) ¿Realmente estamos bajo presión de memoria, o solo reteniendo caché?
(2) Si la presión es real, ¿qué la causa—un proceso, un límite de cgroup o el comportamiento de reclaim del kernel?
(3) ¿La ralentización proviene de I/O de swap, stalls por reclaim directo, o ambos?

Primero: confirma la presión y el tipo de dolor

  • Observa memoria + swap + indicadores de reclaim: free, vmstat, y PSI en /proc/pressure.
  • Observa los mayores consumidores: ordena por RSS con ps, y luego revisa cgroups.
  • Observa los stalls: si/so altos (swap in/out), wa alto, y PSI “some/full”.

Segundo: identifica el límite

  • Si estás en bare metal/VM sin límites de cgroup, el límite es “RAM + swap”.
  • Si estás en un host con contenedores, el límite puede ser un memory.max de cgroup; el swap puede crecer en el host mientras un contenedor se ahoga.
  • Si systemd-oomd está habilitado, puede estar matando (o no matando) según señales PSI y la configuración de la unidad.

Tercero: decide la clase de intervención

  • Fuga o descontrol: arregla la app, reinicia de forma segura, añade medidas de protección (límites/oomd).
  • Trabajo intensivo en caché: ajusta reclaim y evita botones de pánico como “drop caches”.
  • Thrash de swap: reduce el intercambio (swappiness, tuning de memcg) o acelera el swap (zram/NVMe) mientras arreglas la causa real.
  • Sistema mal dimensionado: añade RAM o reduce la concurrencia. Sí, a veces la solución es gastar dinero.

Una idea parafraseada de John Allspaw (operaciones y fiabilidad): “No arreglas incidentes cazando una única ‘causa raíz’; arreglas las condiciones que permitieron la falla.”
Eso es el crecimiento de swap en pocas palabras: suele ser un conjunto de condiciones, no un villano único.

Qué ocurre realmente cuando crece el swap

Linux usa la RAM para más que el heap de tu aplicación. Mantiene datos de archivo en la page cache, rastrea metadatos del sistema de archivos,
y conserva páginas anónimas (la memoria de tus procesos). Cuando la RAM se llena, el kernel reclama memoria: elimina páginas de caché limpias,
escribe páginas sucias de vuelta y, si es necesario, intercambia páginas anónimas al espacio de swap.

El crecimiento del swap no significa automáticamente que el sistema está “sin memoria”. También puede significar que el kernel decidió que algunas páginas estaban frías
y las aparcó en swap para hacer espacio a caché que mejora el rendimiento. Eso suena razonable—hasta que no lo es.

Los tres modos de fallo que realmente verás

  1. Swap benigno: se usa un poco de swap, pero la tasa de swap-in se mantiene baja. La máquina responde. Puedes ignorarlo.
  2. Thrash de swap: las tasas de swap-in/out se disparan, la latencia explota, la CPU muestra tiempo en I/O wait y las tareas interactivas se arrastran.
    Este es tu escenario de “rendimiento se desploma”.
  3. Stalls por reclaim directo sin I/O masivo de swap: el kernel pasa tiempo reclamando páginas; PSI memoria “some/full” sube.
    La caja se siente lenta aun si el I/O de swap no es enorme, especialmente con ciertos workloads y fragmentación de memoria.

En Debian 13, normalmente tendrás un kernel moderno y systemd. Eso es bueno—PSI está disponible, cgroup v2 es frecuente, y systemd-oomd existe. Pero también significa que puedes acabar
con muchos “valores predeterminados inteligentes” interactuando de forma que, individualmente, son inteligentes y, colectivamente, tontos.

Broma corta #1: El swap es como un cajón de trastos—perfecto hasta que necesitas algo rápido y está enterrado bajo menús de comida para llevar.

Hechos y contexto: swap, reclaim y por qué Debian se comporta así

  • Hecho 1: El swap de Linux precede al pensamiento moderno de “RAM barata”; fue diseñado cuando la memoria era escasa y los workloads más pequeños,
    por lo que las políticas de reclaim evolucionaron durante décadas, no meses.
  • Hecho 2: El kernel de Linux distingue entre memoria anónima (páginas de proceso) y memoria respaldada por archivo (page cache).
    Las páginas respaldadas por archivo pueden descartarse; las anónimas necesitan swap (o reclaim mediante kill).
  • Hecho 3: PSI (Pressure Stall Information) llegó para medir el tiempo que las tareas están detenidas por presión de recursos (memoria/CPU/I/O),
    lo cual es más accionable que “memoria libre” al diagnosticar latencia.
  • Hecho 4: cgroup v2 cambió la semántica de control de memoria y convirtió memory.high en un control de estrangulamiento en vez de solo un límite rígido;
    esto importa porque el estrangulamiento puede parecer “lentitud misteriosa”.
  • Hecho 5: El OOM killer es a nivel de kernel y contundente; systemd-oomd es en espacio de usuario y basado en políticas,
    a menudo desencadenado por señales PSI en lugar de esperar al OOM total.
  • Hecho 6: El valor por defecto de vm.swappiness ha cambiado entre eras y distribuciones; no es una declaración moral,
    es una perilla de compensación, y los valores predeterminados están ajustados para “propósito general”, no para tu workload específico.
  • Hecho 7: Intercambiar a NVMe rápido puede “funcionar” pero aún arruinar la latencia cola porque el swap-in suele estar en la ruta crítica para el manejo de peticiones.
  • Hecho 8: Transparent Huge Pages (THP) puede inflar la huella de memoria y complicar el comportamiento de reclaim; para algunas bases de datos es beneficioso,
    para otras es una fuente constante de problemas.
  • Hecho 9: La fragmentación de memoria puede causar fallos de asignación o stalls de reclaim incluso cuando la “memoria libre” parece no nula,
    particularmente para asignaciones contiguas grandes (incluidas huge pages).

Tareas prácticas: comandos, salidas y decisiones (el trabajo real)

Estos no son comandos de “ejecuta esto y te sentirás mejor”. Cada uno es: comando → qué significa la salida → qué decisión tomas.
Ejecútalos en orden hasta que la historia sea obvia.

Tarea 1: Establecer el estado base de memoria y swap

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            62Gi        51Gi       1.2Gi       1.5Gi        10Gi       3.4Gi
Swap:           16Gi       9.8Gi       6.2Gi

Qué significa: “available” es la línea clave, no “free.” Aquí, solo 3.4Gi son fácilmente recuperables sin un gran impacto.
El swap ya está ~60% usado.
Decisión: Continúa: necesitamos ver si el swap se está usando activamente (thrash) o solo está asignado y frío.

Tarea 2: Comprobar actividad viva de swap (thrash vs páginas aparcadas)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 10032128 120064  89216 8675328  12  18    22    40  820 2150 12  6 80  2  0
 1  0 10032256 118720  89216 8675456   0   0     0     8  790 2080 10  5 84  1  0
 3  1 10110464  93248  89216 8629312 420  980   120   640 1100 3200 14  7 62 17  0
 2  1 10135680  90112  89216 8622336 510 1200   180   920 1180 3400 15  8 55 22  0
 1  0 10136576  95040  89216 8622912  10   40    16    88  840 2250 12  6 79  3  0

Qué significa: Mira si/so (swap in/out) y wa (I/O wait). Las muestras del medio muestran I/O de swap pesado y alta espera.
Eso es thrash, no swap benigno.
Decisión: Identifica quién está forzando esto y si la presión es global o dentro de un cgroup.

Tarea 3: Identificar los mayores consumidores de memoria residente (RSS)

cr0x@server:~$ ps -eo pid,comm,rss,vsz,%mem --sort=-rss | head -n 12
  PID COMMAND           RSS    VSZ %MEM
 4121 java          18842340 25011240 29.6
 2337 postgres       9241120 10012560 14.5
 2875 python3        4021152  5120032  6.3
 1998 node           2568896  3101024  4.0
 1552 dockerd         812224  1887744  1.3
  911 systemd-journald 392448   566288 0.6

Qué significa: RSS es el “resident set,” lo que está realmente en RAM. VSZ incluye mapeos que no necesariamente están en memoria residente.
Si un proceso domina el RSS, es tu principal sospechoso.
Decisión: Para procesos grandes, comprueba si el crecimiento de memoria es esperado (heap, caché) o una fuga; también revisa límites de cgroup.

Tarea 4: Confirma si usas cgroup v2 (común en Debian moderno)

cr0x@server:~$ stat -fc %T /sys/fs/cgroup/
cgroup2fs

Qué significa: cgroup v2 está activo. La presión de memoria puede ocurrir dentro de una unidad/contenedor incluso si el host parece “bien”, o viceversa.
Decisión: Inspecciona uso y límites de memoria por unidad o contenedor.

Tarea 5: Encontrar las unidades con peor presión de memoria vía systemd

cr0x@server:~$ systemctl status --no-pager --full user.slice
● user.slice - User and Session Slice
     Loaded: loaded (/usr/lib/systemd/system/user.slice; static)
     Active: active since Mon 2025-12-29 08:12:10 UTC; 2h 21min ago
       Docs: man:systemd.special(7)
      Tasks: 246 (limit: 76800)
     Memory: 14.8G
        CPU: 1h 10min 32.920s
     CGroup: /user.slice
             ├─user-1000.slice
             │ ├─session-4.scope
             │ │ ├─4121 java
             │ │ └─...

Qué significa: systemd puede mostrar memoria por slice. Si un slice o servicio crece descontrolado, puedes contenerlo.
Decisión: Profundiza en la unidad de servicio específica (o alcance del contenedor) y revisa sus controles de memoria.

Tarea 6: Inspeccionar límites de memoria y uso actual para una unidad específica

cr0x@server:~$ systemctl show myapp.service -p MemoryCurrent -p MemoryPeak -p MemoryMax -p MemoryHigh -p ManagedOOMMemoryPressure
MemoryCurrent=20521447424
MemoryPeak=22231822336
MemoryMax=infinity
MemoryHigh=infinity
ManagedOOMMemoryPressure=auto

Qué significa: Sin techo de memoria. Si la app crece, presionará al sistema hacia reclaim y swap.
Decisión: Si este es un host multi-tenant, añade MemoryHigh/MemoryMax como guardarraíles y define una política OOM.

Tarea 7: Leer PSI de presión de memoria (esto se correlaciona con “todo está lento”)

cr0x@server:~$ cat /proc/pressure/memory
some avg10=18.42 avg60=12.01 avg300=6.55 total=922337
full avg10=3.21 avg60=1.70 avg300=0.62 total=120044

Qué significa: “some” es tiempo en que al menos una tarea está parada por memoria. “full” es tiempo en que el sistema está completamente detenido (sin progreso).
Esos valores son lo suficientemente altos para explicar los picos de latencia y las quejas de usuarios.
Decisión: Trata esto como un asunto de fiabilidad, no como un “ajuste opcional.” Reduce la presión o aplica límites.

Tarea 8: Comprobar dispositivos de swap y prioridades (swap lento puede ser fatal)

cr0x@server:~$ swapon --show --bytes
NAME       TYPE      SIZE        USED       PRIO
/dev/nvme0n1p3 partition 17179865088 10522624000 -2

Qué significa: El swap está en una partición NVMe, pero la prioridad es baja (-2). Si también tienes zram u otros dispositivos de swap, la prioridad importa.
Decisión: Si el swap es inevitable, prefiere swap más rápido (a menudo zram para ráfagas) y establece prioridades intencionales.

Tarea 9: Observar faltas de página mayores y fallos de swap por proceso

cr0x@server:~$ pidstat -r -p 4121 1 3
Linux 6.12.0 (server)  12/29/2025  _x86_64_  (32 CPU)

09:41:10      UID       PID  minflt/s  majflt/s     VSZ     RSS   %MEM  Command
09:41:11     1000      4121   1200.00     35.00 25011240 18842340  29.6  java
09:41:12     1000      4121   1305.00     42.00 25011240 18850012  29.7  java
09:41:13     1000      4121   1104.00     38.00 25011240 18861020  29.7  java

Qué significa: Las faltas mayores (majflt/s) a menudo implican I/O de disco para traer páginas de vuelta—el swap-in es una causa común.
Faltas mayores sostenidas con actividad de swap son una señal inequívoca de thrash.
Decisión: Reduce el working set o añade RAM; considera también ajustar el heap y límites de memoria.

Tarea 10: Comprobar estadísticas de reclaim del kernel (¿estamos requiriendo page cache?)

cr0x@server:~$ egrep 'pgscan|pgsteal|pswpin|pswpout' /proc/vmstat | head
pgscan_kswapd 18223344
pgscan_direct 992311
pgsteal_kswapd 17199812
pgsteal_direct 801223
pswpin 621044
pswpout 1200033

Qué significa: Un pgscan_direct alto sugiere reclaim directo (tareas detenidas). Altos pswpin/out indican churn de swap.
Decisión: Si el reclaim directo es significativo, la presión está afectando la latencia. Prioriza reducir el working set y añadir límites; ajustar solo swappiness no te salvará.

Tarea 11: Verificar swappiness y otros knobs de VM (no ajustes a ciegas)

cr0x@server:~$ sysctl vm.swappiness vm.vfs_cache_pressure vm.dirty_ratio vm.dirty_background_ratio
vm.swappiness = 60
vm.vfs_cache_pressure = 100
vm.dirty_ratio = 20
vm.dirty_background_ratio = 10

Qué significa: swappiness=60 es un valor por defecto de propósito general. En servidores sensibles a la latencia, puede ser demasiado propenso a swapear bajo presión.
Los ratios de dirty afectan ráfagas de writeback; un mal ajuste puede crear congestión de I/O que amplifica el dolor del swap.
Decisión: Si observas thrash real (alto si/so), merece la pena probar bajar swappiness (con cuidado) y asegurar que los ajustes de dirty no provoquen tormentas de writeback.

Tarea 12: Comprobar configuración de THP (puede inflar la huella y estancar reclaim)

cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

Qué significa: THP está habilitado “always”. Eso puede estar bien, pero para algunos workloads (notablemente ciertas bases de datos/caches) provoca picos de latencia o aumento de memoria.
Decisión: Si ves stalls de reclaim y latencia impredecible, prueba cambiar a madvise (o never) de forma controlada.

Tarea 13: Confirmar correlación entre espera de I/O y I/O de swap

cr0x@server:~$ iostat -xz 1 3
Linux 6.12.0 (server)  12/29/2025  _x86_64_  (32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.10    0.00    6.50   18.40    0.00   63.00

Device            r/s     w/s   rKB/s   wKB/s  avgrq-sz avgqu-sz   await  svctm  %util
nvme0n1         85.00  140.00  9200.0 18000.0    210.0     4.20   18.50   0.45  92.00

Qué significa: %iowait está alto y el NVMe está cerca de saturación. El I/O de swap compite con todo lo demás y estropea la latencia cola.
Decisión: Si el swap está en el mismo dispositivo que tu base de datos o logs, estás invitando al cross-talk. Separa dispositivos o reduce el swapping.

Tarea 14: Revisar journal y logs del kernel por eventos OOM y de presión de memoria

cr0x@server:~$ journalctl -k --since "1 hour ago" | egrep -i 'oom|out of memory|memory pressure|kswapd' | tail -n 20
Dec 29 09:02:14 server kernel: oom_reaper: reaped process 4121 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
Dec 29 09:02:14 server kernel: Out of memory: Killed process 4121 (java) total-vm:25011240kB, anon-rss:18842340kB, file-rss:10240kB, shmem-rss:0kB, UID:1000 pgtables:52000kB oom_score_adj:0

Qué significa: Si ves kills por OOM, no “arreglaste el swap,” sobreviviste a ello. También ten en cuenta: el OOM del kernel es el último recurso, a menudo después de un largo thrash.
Decisión: Pon políticas en su lugar (límites, oomd) para fallar más rápido y con previsibilidad, y arregla el dimensionamiento/fuga de memoria.

Tarea 15: Si ejecutas contenedores, inspecciona eventos de memoria (el asesino silencioso)

cr0x@server:~$ cat /sys/fs/cgroup/system.slice/docker.service/memory.events
low 0
high 1242
max 0
oom 0
oom_kill 0

Qué significa: Muchos eventos “high” indican que el grupo está alcanzando memory.high y se está estrangulando con frecuencia. Eso parece lentitud aleatoria.
Decisión: Aumenta el umbral de memory.high, reduce la concurrencia del workload, o corrige la huella del servicio. El estrangulamiento es útil, pero sigue siendo doloroso.

Soluciones que realmente ayudan (y por qué)

Hay dos categorías de “solución”: estructurales y tácticas. Las estructurales abordan por qué el working set no cabe en RAM. Las tácticas reducen el radio de impacto
cuando sucede igual. Quieres ambas. Y quieres evitar el tuning por mitos.

1) Coloca guardarraíles de memoria donde vive el problema (unidades systemd / cgroups)

Si un servicio puede comerse el host, tarde o temprano lo hará—por accidente, por un pico de tráfico, o por una característica desplegada a las 4:55 PM un viernes.
En Debian con systemd, usa controles de memoria a nivel de unidad.

cr0x@server:~$ sudo systemctl edit myapp.service
[Service]
MemoryHigh=18G
MemoryMax=22G
ManagedOOMMemoryPressure=kill
ManagedOOMMemoryPressureLimit=20%

Cómo pensar en ello:
MemoryHigh es “comenzar a aplicar presión” (reclaim/estrangulamiento). MemoryMax es “parada dura.” Si tu servicio no puede sobrevivir al reclaim,
prefieres matarlo y reiniciarlo antes que dejarlo envenenar todo el nodo.

2) Configura systemd-oomd intencionalmente (o desactívalo explícitamente)

systemd-oomd no es el OOM killer del kernel. Es un motor de políticas basado en métricas de presión. Esto puede ser fantástico—si lo configuras para tu topología.
También puede matar lo equivocado si tratas los valores por defecto como evangelio.

cr0x@server:~$ systemctl status systemd-oomd --no-pager
● systemd-oomd.service - Userspace Out-Of-Memory (OOM) Killer
     Loaded: loaded (/usr/lib/systemd/system/systemd-oomd.service; enabled)
     Active: active (running) since Mon 2025-12-29 08:12:11 UTC; 2h 35min ago

Decisión: Si ejecutas un host multi-servicio, habilitar oomd con políticas por servicio correctas suele ser mejor que esperar al OOM del kernel.
Si ejecutas un appliance de propósito único con control fuerte a nivel de app, puedes optar por desactivar oomd para evitar kills sorpresa—pero entonces debes tener otras protecciones.

3) Bajar swappiness para servicios sensibles a latencia (pero no lo tomes por solución mágica)

vm.swappiness controla qué tan agresivamente el kernel intercambiará memoria anónima en lugar de reclamar caché de archivos.
En servidores donde la latencia importa más que exprimir cada acierto de caché, una swappiness baja puede ayudar a reducir el churn de swap.
No hace que un host subdimensionado quepa mágicamente.

cr0x@server:~$ sudo sysctl -w vm.swappiness=10
vm.swappiness = 10
cr0x@server:~$ sudo tee /etc/sysctl.d/99-swappiness.conf >/dev/null <<'EOF'
vm.swappiness=10
EOF

Decisión: Si observas thrash real (alto si/so), bajar swappiness merece prueba.
Si observas PSI “full” alto y reclaim directo, aún necesitas reducir demanda de memoria o establecer límites.

4) Arregla primero el mayor working set: limitar heaps, ajustar cachés, evitar doble-caché

El culpable más común de “swap crece para siempre” es una aplicación con heap o caché elástico que nunca recibió un techo.
Java, Node, Python con cachés agresivos y bases de datos pueden comportarse así.

  • Java: establece -Xmx de forma inteligente; no lo dejes flotar a “lo que el SO permita”.
  • PostgreSQL: mantén shared_buffers razonable y recuerda que la page cache del SO ya es una caché.
  • Elasticsearch / caches: limita la memoria; no dejes que la caché sea “infinita” solo porque el host tiene swap.

La trampa clásica es el doble-caché: tu aplicación cachea datos en heap mientras el kernel cachea los mismos datos en page cache. Pagas dos veces y luego swap.
Una jerarquía de memoria no es un buffet.

5) Considera zram para presión por ráfagas (especialmente en VMs pequeñas)

Si tu host a veces está justo de memoria, zram (swap comprimido en RAM) puede absorber ráfagas sin golpear el disco.
No es gratis—la compresión cuesta CPU—pero puede ser una ganancia neta cuando la latencia de disco es el enemigo mayor.

cr0x@server:~$ sudo apt-get update
Hit:1 deb.debian.org stable InRelease
Reading package lists... Done
cr0x@server:~$ sudo apt-get install -y zram-tools
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
  zram-tools
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.

Decisión: Si el thrash de swap ocurre en un disco lento o almacenamiento compartido, zram puede ser una mitigación táctica fuerte.
Si tus workloads ya están limitados por CPU, zram puede empeorar las cosas. Mide.

6) Coloca el swap en el almacenamiento correcto, con las expectativas correctas

Swap en discos giratorios es viajar al 2009. Swap en dispositivos de bloque en red es una aventura que no pediste.
Swap en NVMe puede ser viable, pero si comparte el dispositivo con bases de datos críticas en latencia, aún puede destruir la latencia cola.

Buenas prácticas:

  • Prefiere un dispositivo de swap dedicado o al menos separado de volúmenes con DB WAL/log intensos.
  • Asegura que el swap se habilite temprano y tenga prioridades correctas si existen múltiples backends de swap.
  • Dimensiona el swap correctamente. Si “necesitas” un swap enorme para mantenerte arriba, estás enmascarando un problema de capacidad.

7) Ajusta writeback de páginas sucias para evitar tormentas de I/O que amplifiquen el dolor del swap

Presión de memoria + ráfagas de writeback = mal momento. Si las páginas sucias se acumulan, el kernel fuerza writeback, lo que puede saturar I/O justo cuando el swap-in lo necesita.
Así obtienes un servidor que está tanto swap-eando como “aleatoriamente” lento incluso después de que el swap deja de crecer.

cr0x@server:~$ sudo sysctl -w vm.dirty_background_ratio=5 vm.dirty_ratio=10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

Decisión: Bajar los ratios puede suavizar el writeback en sistemas con escrituras en ráfagas y sensibilidad a la latencia.
Pero no ajustes esto aisladamente; necesitas revisar el rendimiento de almacenamiento y los patrones de escritura de la aplicación.

8) Evita usar “drop caches” como hábito

Sí, echo 3 > /proc/sys/vm/drop_caches hará que la “memoria libre” parezca mejor. También aniquilará caché útil y puede causar una estampida de relecturas.
Es una herramienta de diagnóstico de último recurso, no una solución. Si la necesitas semanalmente, estás ejecutando producción a base de corazonadas.

9) No desactives swap a menos que estés preparado para muertes abruptas

Apagar el swap puede reducir el thrash, pero cambia el modo de fallo de “lento” a “matado.” A veces eso es deseable. A menudo no.
Si desactivas swap, necesitas:

  • límites estrictos de memoria por servicio
  • una política clara de reinicio
  • alertas al acercarse a los límites y sobre PSI
  • un plan para picos de tráfico

Broma corta #2: Desactivar el swap para arreglar la presión de memoria es como quitar el detector de humo porque su pitido molesta.

10) Admite cuando la máquina es demasiado pequeña

Si tu working set de estado estable excede la RAM, o vas a swapear, estancar o matar procesos. El tuning puede elegir qué miseria prefieres;
no puede reescribir la aritmética. Añade RAM, reduce la concurrencia, shardea la carga o mueve servicios pesados fuera del nodo.

Tres microhistorias corporativas (cómo falla esto en la vida real)

Microhistoria 1: El incidente causado por una suposición errónea

Un equipo ejecutaba una API orientada al cliente en hosts Debian con “mucha memoria.” Tenían swap configurado porque “es más seguro.”
Sus paneles monitoreaban CPU, tasa de peticiones y utilización de disco. La memoria era una sola línea: “free.”
Parecía bien la mayor parte del tiempo. Luego la latencia se dobló durante diez minutos, varias veces al día, sin patrón obvio.

La suposición errónea fue sutil: creían que si el host no estaba sin memoria, la memoria no era el problema.
El uso de swap aumentó gradualmente durante picos de tráfico, pero como nada se caía, no generó urgencia.
Mientras tanto, el kernel estaba intercambiando páginas frías de un servicio Java. Bajo ráfagas posteriores, esas páginas dejaron de estar frías.
Los swap-ins aterrizaron en el mismo volumen NVMe usado por el WAL de la base de datos.

El incidente se manifestó como “la base de datos se pone lenta,” porque eso fue lo que la gente vio primero: consultas esperando I/O y bloqueos.
Pero la condición raíz fue presión de memoria provocando I/O de swap, que compitió con el WAL y amplificó la latencia cola.
El equipo persiguió índices y pools de conexiones durante una semana.

La solución no fue exótica. Añadieron alertas basadas en PSI, bajaron swappiness, separaron el swap del volumen de base de datos,
y—lo más importante—fijaron el heap de Java y los caches en proceso. Después de eso, el swap se mantuvo mayormente estable,
y cuando volvió la presión, fue evidente temprano en lugar de convertirse en un whodunit diario.

Microhistoria 2: La optimización que salió mal

Otra organización quiso “usar la memoria eficientemente” y se empeñó en maximizar la page cache para workloads de lectura.
Alguien sugirió aumentar swappiness “para que Linux mantenga caliente la caché de archivos.” También habilitaron Transparent Huge Pages por todas partes
porque “las páginas más grandes son más rápidas.”

En teoría sonaba limpio: más aciertos de caché, menos lecturas de disco. En la práctica, dos servicios compartían el host:
una API intensiva en caché y un job por lotes que picos de memoria durante procesamiento diario.
Con swappiness alto, las páginas anónimas se intercambiaban agresivamente bajo presión; con THP, la huella de memoria se volvió menos predecible.
El job por lotes disparó reclaim; el reclaim disparó swap; el swap robó ancho de banda de I/O; y la API interactiva pagó la factura.

Lo realmente doloroso: la optimización mejoró benchmarks de throughput en aislamiento.
Solo falló bajo condiciones de workload mixto, que es el estado por defecto de los servidores corporativos.
El equipo vio “buena ratio de cache hits” y asumió éxito mientras la latencia de clientes empeoraba.

El rollback fue directo: swappiness de nuevo abajo, THP en madvise, y el job por lotes movido a un scope systemd con un tope duro de memoria.
También dejaron de co-ubicar procesamiento por lotes con APIs sensibles a latencia en la misma clase de nodo.
La “optimización” no era malvada; estaba mal aplicada y sin límites.

Microhistoria 3: La práctica aburrida pero correcta que salvó el día

Un equipo de plataforma gestionaba una flota de servidores Debian que alojaban servicios internos: logs, métricas, un puñado de APIs,
y un par de workers en segundo plano. Nada glamuroso. Tenían una regla aburrida: cada servicio obtiene una unidad systemd con límites de memoria, y cada nodo
exporta métricas PSI con alertas. Sin excepciones. Los desarrolladores se quejaban. En silencio, todos cumplían.

Una tarde, un despliegue introdujo una fuga de memoria en un worker Python. El RSS subió lentamente en unas horas.
En hosts sin guardarraíles, este tipo de fuga se convierte en una caída en cámara lenta: swap crece, la latencia sube, la gente culpa a la red,
y finalmente algo se cae.

Aquí, el worker alcanzó MemoryHigh primero y empezó a ser estrangulado. PSI se disparó para la unidad, no para todo el nodo.
Las alertas saltaron con contexto claro: “unidad worker en presión de memoria” en lugar de “host lento.”
systemd-oomd mató el worker cuando la presión superó el límite configurado. systemd lo reinició, restaurando el servicio.

Nadie celebró. Ese es el punto. El sistema degradó de forma predecible y se recuperó sin convertir todo el nodo en un horno de swap.
El trabajo de seguimiento fue focalizado: arreglar la fuga, añadir una prueba de regresión y mantener los guardarraíles. Lo aburrido ganó. De nuevo.

Errores comunes: síntoma → causa raíz → solución

1) “Swap usado es alto, así que nos quedamos sin memoria”

Síntoma: Swap no es cero o está creciendo, pero el sistema se siente bien.
Causa raíz: Swap benigno: páginas anónimas frías intercambiadas; tasa de swap-in no significativa.
Solución: Revisa vmstat por si/so y PSI. Si swap-in está casi en cero y PSI es bajo, no lo toques.

2) “Lo arreglaremos poniendo swappiness=1”

Síntoma: El swap se reduce, pero el sistema empieza a OOMear o a estancarse durante el reclaim.
Causa raíz: El working set sigue sin caber; solo cambiaste qué páginas se sacrifican. Swappiness bajo no previene la presión de memoria.
Solución: Limita la memoria de la aplicación, establece memory.high/memory.max, reduce la concurrencia o añade RAM. Usa swappiness como ajuste fino, no como paracaídas.

3) “Desactivamos swap y estamos seguros”

Síntoma: Ya no hay thrash lento, pero kills y reinicios súbitos; a veces el OOM del kernel elimina el servicio equivocado.
Causa raíz: El modo de fallo cambió de “degradado” a “abrupto.” Sin política, es un caos.
Solución: Si desactivas swap, aplica límites por servicio y adopta una política predecible de kill/restart (systemd-oomd o supervisión de servicios).

4) “Es rendimiento del disco; afinemos el sistema de archivos”

Síntoma: Alto iowait, alta utilización de almacenamiento, pero el evento raíz es presión de memoria.
Causa raíz: I/O de swap compite con I/O real; el disco parece culpable porque está siendo usado como RAM de emergencia.
Solución: Reduce el swapping primero. Separa el dispositivo de swap si hace falta. Luego reevalúa el tuning de disco.

5) “Simplemente borramos caches”

Síntoma: Mejora a corto plazo, luego peor rendimiento y más I/O.
Causa raíz: Destruiste page cache útil; el sistema debe releer datos, incrementando I/O y presión.
Solución: No lo hagas salvo experimento controlado. Arregla el dimensionamiento y la huella de la app.

6) “El contenedor tiene límite de memoria, así que el host está protegido”

Síntoma: El swap del host crece; un contenedor se ralentiza; no hay eventos claros de OOM.
Causa raíz: Estrangulamiento por memory.high y reclaim dentro del cgroup; la page cache del host y otros servicios aún compiten; los límites no impiden que la presión afecte a vecinos.
Solución: Configura tanto memory.high como memory.max sensatamente; aísla servicios ruidosos; observa cgroup PSI y memory.events.

7) “El swap es lento porque está en NVMe; eso no puede ser”

Síntoma: NVMe es rápido pero la latencia es terrible al swapear.
Causa raíz: El swap-in está en la ruta crítica; la cola y la contención dañan la latencia cola incluso en dispositivos rápidos.
Solución: Deja de swapear reduciendo el working set. Usa zram para ráfagas si la CPU lo permite. Separa el swap de rutas I/O calientes.

Listas de verificación / plan paso a paso

Checklist A: Cuando estás de guardia y la caja está “lenta”

  1. Ejecuta free -h y registra “available” y swap usado.
  2. Ejecuta vmstat 1 10. Si si/so se dispara o wa es alto, sospecha thrash de swap.
  3. Lee /proc/pressure/memory. Si “full avg10” es no trivial, trátalo como un incidente.
  4. Revisa iostat -xz 1 3 para ver si el swap está saturando tu disco principal.
  5. Encuentra los mayores consumidores de RSS con ps; confirma con métricas de servicio/cgroup.
  6. Si un servicio único se descontrola: reinícialo de forma segura y añade un límite temporal (MemoryHigh/Max) para evitar recurrencias.
  7. Abre una tarea de seguimiento: plan de capacidad, límites por servicio y alertas PSI.

Checklist B: Soluciones permanentes en un host Debian 13 multi-servicio

  1. Inventaria servicios y establece límites de memoria: MemoryHigh para presión suave, MemoryMax para parada dura.
  2. Decide política: usa systemd-oomd para matar unidades bajo presión sostenida, o deja al OOM del kernel como último recurso (no recomendado para multi-tenant).
  3. Fija swappiness a un valor probado (a menudo 10–30 para hosts sensibles a latencia).
  4. Evalúa zram para absorber ráfagas; valida margen de CPU.
  5. Coloca swap en almacenamiento apropiado; evita compartir con DB/WAL con mucho I/O.
  6. Valida comportamiento de THP; cambia a madvise si reduce picos de latencia.
  7. Alerta sobre PSI memoria “some/full” y sobre tasas de swap-in/out, no solo swap usado.

Checklist C: Si sospechas una fuga

  1. Captura el crecimiento de RSS a lo largo del tiempo: snapshots con ps o pidstat -r.
  2. Confirma comportamiento de swap-in vía faltas mayores: pidstat -r majflt.
  3. Añade un tope duro de memoria para evitar impacto host-wide mientras debuggeas.
  4. Reinicia el servicio para restaurar estabilidad; documenta el incidente con evidencia: tendencia RSS, PSI, vmstat y logs.

Preguntas frecuentes

1) ¿El uso de swap siempre es malo?

No. Lo que te mata es la actividad de swap. Si el swap usado es alto pero vmstat muestra si/so casi en cero y PSI es bajo, probablemente está bien.

2) ¿Cuál es la mejor métrica única para “la memoria está dañando la latencia”?

PSI de presión de memoria (/proc/pressure/memory). Te dice cuánto tiempo las tareas están paradas. Se correlaciona con “los usuarios están molestos” mejor que “RAM libre.”

3) ¿Debería bajar swappiness en servidores Debian 13?

A menudo sí para servicios sensibles a latencia, pero solo tras confirmar que ves thrash de swap. Empieza con 10–30, mide swap-in/out y PSI.
No uses swappiness para ocultar un working set sobredimensionado.

4) ¿Debería desactivar el swap completamente?

Solo si aceptas kills abruptos por OOM y tienes guardarraíles (límites por servicio, políticas de reinicio y monitorización). Desactivar swap puede mejorar la previsibilidad
en algunos clusters, pero no es una mejora universal.

5) ¿Qué es mejor: OOM killer del kernel o systemd-oomd?

Para hosts multi-servicio, systemd-oomd con políticas explícitas por unidad suele ser mejor porque puede actuar antes basándose en presión y dirigirse a la unidad correcta.
El OOM del kernel es el último recurso y puede tumbar algo crítico después de minutos de thrash.

6) ¿Por qué el rendimiento cae incluso cuando el I/O de swap no es masivo?

Reclaim directo y stalls. El kernel puede pasar tiempo escaneando y reclamando páginas mientras las tareas esperan. PSI “some/full” normalmente lo mostrará.
La fragmentación y las interacciones con THP también pueden contribuir.

7) ¿Es zram seguro para servidores?

Sí, cuando se usa deliberadamente. Es ideal para presión por ráfagas y para sistemas donde el swap en disco es lento o está disputado.
Consume CPU; si estás limitado por CPU, puedes simplemente mover el cuello de botella.

8) Mi contenedor tiene límite de memoria; ¿por qué el host está swap-eando?

Porque el host todavía gestiona memoria global y puede intercambiar por presión general. Además, el estrangulamiento de cgroup (memory.high) puede causar lentitud sin eventos OOM.
Inspecciona memory.events y PSI a nivel de unidad.

9) ¿Cuánto swap debería tener un servidor Debian 13?

Suficiente para manejar ráfagas cortas y permitir un comportamiento ordenado, no tanto como para ocultar un sobredimensionamiento crónico. No hay un número universal.
Si dependes regularmente de swap profundo, necesitas más RAM o reducir working sets.

10) ¿Por qué el swap sigue creciendo y nunca disminuye?

Linux no trae agresivamente las páginas de vuelta a RAM a menos que se accedan. Si las páginas intercambiadas están realmente frías, permanecen en swap.
Eso no es un bug. Se vuelve un problema cuando esas páginas dejan de estar frías y empiezas a swapear-in bajo carga.

Conclusión: próximos pasos prácticos

Si el swap está creciendo y el rendimiento en Debian 13 se desploma, trátalo como lo que es: presión de memoria que deriva en latencia y contención de I/O.
No discutas con el gráfico de swap. Úsalo como pista y luego prueba el modo de fallo con actividad de swap y PSI.

  1. Ahora mismo: ejecuta vmstat, free, PSI y iostat. Decide si tienes thrash o swap benigno.
  2. En una hora: identifica el mayor consumidor de RSS y si está acotado por cgroups/unidades systemd.
  3. En el día: añade guardarraíles de memoria (MemoryHigh/MemoryMax) y una política OOM deliberada.
  4. En la semana: arregla el working set (tamaño de heap/cachés), evalúa zram para ráfagas y asegúrate de que la colocación del swap no compita con I/O crítico.
  5. Siempre: alerta sobre PSI y swap-in/out, no solo swap usado. Tus usuarios experimentan stalls, no contabilidad.

El estado final que quieres no es “swap es cero.” Es “la presión es visible, acotada y recuperable”—y la máquina permanece responsiva cuando importa.

← Anterior
Mitos de la VRAM: cuándo importan 8/12/16 GB y cuándo es solo un número
Siguiente →
Versiones del módulo ZFS: evitar conflictos entre el kernel y ZFS

Deja un comentario