Ajuste de parámetros del kernel en Ubuntu 24.04: los 5 sysctls que importan (y los 10 que no) (caso #42)

¿Te fue útil?

Te han alertado porque la latencia p99 se duplicó, la API “funciona en mi portátil” y alguien en un hilo copia-pega un conjuro sysctl -w de 2013. La tentación es tunear el kernel como si fuera un coche de carreras.
En producción es más parecido a ajustar un tren de carga: las perillas pequeñas importan, las grandes pueden descarrilarte y la mayoría son placebo.

Ubuntu 24.04 se entrega con un kernel moderno y valores por defecto conservadores que suelen ser adecuados. La clave es saber cuáles cinco sysctls merece la pena tocar, cuándo importan y cómo demostrar que ayudaste en vez de simplemente cambiar números.

Reglas de compromiso: cómo tunear sin dañarte

Ajustar el kernel no es un pasatiempo. Trátalo como un cambio en producción: necesitas una hipótesis, una métrica, un rollback
y una definición clara de “mejor”. Si no, solo estarás reorganizando sysctls hasta que el incidente termine por razones no relacionadas.

1) Cambia una dimensión a la vez

Si cambias buffers TCP, umbrales de páginas sucias y dimensionamiento de conntrack en un solo despliegue, has creado una novela de misterio.
A los SRE no les pagan para leer novelas de misterio a las 03:00.

2) Prefiere valores por defecto conscientes de la carga en vez de la tradición

Los límites de tu sistema suelen estar en otro sitio: saturación de CPU, latencia de almacenamiento, timeouts de DNS, bloqueo por cabecera de línea
o retropresión a nivel de aplicación. Los sysctls pueden ayudar, pero solo cuando puedes nombrar el cuello de botella.

3) Haz los cambios persistentes de la manera correcta

Ubuntu 24.04 carga sysctls desde /etc/sysctl.conf y /etc/sysctl.d/*.conf.
Pon tus cambios en un archivo dedicado como /etc/sysctl.d/99-local-tuning.conf y documenta por qué.
No “simplemente ejecutes sysctl -w” en un servidor pet y lo des por terminado.

4) Entiende el radio de impacto

Algunos sysctls son por espacio de nombres, otros son globales. Los contenedores pueden complicar esto.
Si afinás el host, podrías cambiar el comportamiento de todos los pods, VM o servicios que usen ese kernel.

5) No tunees para compensar falta de capacidad

No puedes resolver una NIC insuficiente, una matriz de discos saturada o una base de datos que hace full table scans con sysctls.
El tuning es para la fricción. No para la física.

Una cita para mantenerte honesto: La esperanza no es una estrategia. — idea parafraseada a menudo atribuida a líderes de operaciones.
En el tuning del kernel, “esperar” se ve como “pongámoslo en 1.000.000 y ya veremos”.

Hechos e historia que explican los valores por defecto actuales

  • La interfaz “sysctl” existe desde antes de los contenedores Linux. Muchos controles se diseñaron para máquinas monocliente; en hosts multiinquilino, lo “seguro” puede convertirse en “vecino ruidoso”.
  • El escalado de ventana TCP (RFC 1323) hizo relevante el dimensionamiento de buffers. Antes de eso, enlaces con gran producto ancho de banda-retardo no podían rellenarse eficientemente sin importar cuánto ajustaras.
  • Linux pasó de “tunearlo todo” a “autotunar la mayoría de cosas”. Los kernels modernos dimensionan dinámicamente muchos buffers TCP, así que recetas estáticas antiguas a menudo no hacen nada.
  • El modelo de writeback de páginas sucias se ha afinado durante décadas. Estos controles existen porque almacenar escrituras en buffer puede mejorar el rendimiento, pero demasiado buffering provoca picos de latencia espectaculares.
  • vm.swappiness se hizo famoso en parte porque los portátiles intercambiaban páginas en la era temprana de Linux de escritorio. Los servidores heredaron la tradición, incluso cuando no encajaba.
  • Conntrack no siempre fue “por defecto” en los inicios. El firewall stateful y NAT convirtieron el seguimiento de conexiones en una preocupación clave de escalado para muchas flotas.
  • Los puertos efímeros solían colisionar más a menudo en clientes de alta QPS. El rango de puertos y el comportamiento TIME-WAIT se volvieron temas operativos cuando surgieron service meshes y reintentos agresivos.
  • La extenuación de descriptores de fichero siempre ha sido una de las diez principales causas de incidentes. No porque Linux sea frágil, sino porque el software es creativo filtrando sockets bajo fallos parciales.

Broma #1: El tuning del kernel es como sazonar una sopa: siempre puedes añadir más sal, pero no puedes quitarla cuando los clientes ya están gritando.

Guía rápida de diagnóstico (primero/segundo/tercero)

Antes de tocar sysctls, encuentra el cuello de botella. Esta es la “escalera de triaje” más rápida que he usado en flotas Ubuntu:
reduce la lista de sospechosos en minutos.

Primero: ¿es CPU, presión de memoria o cola de ejecutables?

  • Comprueba carga vs uso de CPU: alta carga con bajo uso de CPU a menudo significa espera de I/O o contención de locks.
  • Revisa fallos mayores y swapping: si estás haciendo paging, primero tienes un problema de memoria.
  • Verifica la longitud de la cola de ejecución: si está consistentemente por encima de los núcleos de CPU, estás limitado por CPU o por el planificador.

Segundo: ¿es latencia de almacenamiento o bloqueos por writeback?

  • Busca alta await de disco, saturación y picos en flush/writeback.
  • Busca crecimiento de páginas sucias y tormentas de sync súbitas (especialmente en bases de datos, ráfagas de logging o clientes NFS).

Tercero: ¿es colas de red, descartes o tracking de conexiones?

  • Revisa retransmisiones y descartes (convierten el throughput en tristeza).
  • Revisa overflow de backlog de sockets y descartes en la cola de listen.
  • Revisa el conteo de conntrack vs el máximo y errores de “insert failed”.

Si haces esto en orden, evitarás el error clásico: tunear buffers TCP cuando el problema real es un NVMe estrangulado o una CPU ocupada por cifrado.

Los 5 sysctls que importan (la mayor parte del tiempo)

Estos son los controles que realmente veo mover métricas de producción en Ubuntu 24.04 cuando hay un cuello de botella claro.
No son magia. Son palancas que tiras cuando la medición dice que debes hacerlo.

1) fs.file-max — techo de manejadores de archivos a nivel del sistema

Si ejecutas servicios con muchas conexiones (proxies, brokers de mensajes, frontends HTTP ocupados),
los descriptores de archivo son oxígeno. Cuando se agotan, las cosas no degradan con gracia; fallan de maneras que parecen errores de conexión “aleatorios”.

Los valores por defecto de Ubuntu suelen ser adecuados para cargas moderadas, pero flotas grandes y nodos con muchas conexiones pueden alcanzar el límite.
Además: el límite del kernel es solo la mitad de la historia; los límites por proceso (ulimit -n) y los límites de systemd a menudo muerden primero.

Cuándo cambiarlo

  • Errores como “Too many open files” en los logs.
  • Balanceadores/proxies con decenas de miles de sockets concurrentes por proceso.
  • Sistemas con alta rotación y muchos watchers (inotify) o muchas conexiones de corta duración.

Cómo configurarlo de forma segura

Auméntalo a un valor que puedas justificar, no “infinito”. Monitoriza el uso de descriptores y deja margen.
El kernel necesita memoria para las estructuras de archivos; en máquinas pequeñas, valores enormes son performativos.

2) net.core.somaxconn — techo de backlog de listen

Esto limita cuántas conexiones pueden ponerse en cola para aceptación en un socket en escucha (con matices: el backlog en la llamada listen() de la aplicación y el comportamiento del kernel también importan). Si tienes tráfico en ráfagas y un bucle de accept que no puede seguir el ritmo momentáneamente,
un backlog bajo convierte ráfagas en drops de SYN o conexiones rechazadas.

Cuándo cambiarlo

  • Ráfagas cortas causan fallos de conexión mientras la CPU no está saturada.
  • Reverse proxies y gateways API con ingreso en ráfagas.
  • Servicios que hacen TLS costoso y no pueden aceptar con rapidez durante tormentas.

Cómo configurarlo de forma segura

Súbelo a 4096 o 8192 para frontends ocupados, y luego verifica si redujiste los drops de listen.
Backlogs más grandes pueden ocultar lentitud de la aplicación; no lo uses como sedante.

3) net.ipv4.ip_local_port_range — puertos efímeros para clientes

Esto importa para sistemas que inician muchas conexiones salientes: clientes API, sidecars de service mesh,
gateways NAT, scrapers y cualquier cosa que haga reintentos agresivos. Si te quedas sin puertos efímeros, obtienes fallos de connect(),
y la aplicación a menudo interpreta eso como “el remoto está caído”, añadiendo reintentos y empeorando la situación.

Cuándo cambiarlo

  • Alto QPS saliente con conexiones de corta duración.
  • Nodos NAT o proxy donde muchos clientes internos comparten el egress.
  • Acumulación significativa de TIME-WAIT y no puedes migrar a reutilización de conexiones rápidamente.

Cómo configurarlo de forma segura

Amplía el rango (por ejemplo, a 10240 60999 o similar) manteniéndote alejado de puertos reservados.
Luego verifica el uso de puertos y confirma que no estás enmascarando una mala reutilización de conexiones.

4) vm.swappiness — preferencia de política de swapping

Este es el control que todos tocan primero, y por lo general está mal. Pero importa en un caso muy común en servidores:
cuando tienes mucha RAM, pero el kernel decide swapear páginas poco usadas, y servicios sensibles a la latencia las traen de vuelta en el peor momento posible.

En muchas cargas modernas de servidor, una swappiness baja (a menudo 1 a 10) reduce fallos mayores sorpresa,
asumiendo que tienes suficiente memoria y no usas swap como extensión de capacidad.

Cuándo cambiarlo

  • Picos de latencia correlacionados con fallos mayores de página o swap-ins.
  • Tienes swap habilitado “por si acaso” y quieres que sea último recurso, no rutina.
  • Nodos de bases de datos donde el cache es importante y el thrash de swap es letal.

Cómo configurarlo de forma segura

No lo pongas a 0 por reflejo. Usa un valor bajo y observa el comportamiento de reclaim.
Si estás limitado de memoria, swappiness no es la solución real: añade capacidad o reduce carga.

5) vm.dirty_ratio y vm.dirty_background_ratio — buffering de writeback y distribución del dolor

Sí, son dos sysctls, pero es una sola decisión. Estos gobiernan cuánta data sucia (modificada, aún no escrita) permitirá el kernel
antes de forzar el writeback, y cuándo debe empezar el writeback en background.

Los ratios por defecto pueden estar bien en nodos de propósito general. En sistemas con muchas escrituras, pueden crear comportamiento de “silencio por un tiempo y luego todo se bloquea”:
los buffers se llenan y el kernel fuerza presión de writeback de forma casi síncrona sobre hilos desafortunados. La latencia pasa de suave a dentada.

Cuándo cambiarlo

  • Cargas con muchas escrituras y acantilados periódicos de latencia.
  • Nodos de agregación de logs, pipelines de ingestión, workers de CI que escriben artefactos.
  • Sistemas con ráfagas rápidas (en memoria) pero almacenamiento de respaldo más lento (bloque de red, discos giratorios, RAID sobrecargado).

Cómo configurarlo de forma segura

Para muchos servidores, reducir los ratios (o usar los equivalentes basados en bytes) suaviza el writeback y reduce la latencia de cola.
Pero si te pasas hacia abajo, limitarás el rendimiento innecesariamente. Este es uno que debes validar con métricas reales: bytes sucios, writeback,
latencia de almacenamiento y p99 de la aplicación.

Los 10 sysctls que no importan (la mayor parte del tiempo)

Estos son los controles que aparecen en posts de blog, gists de “hardening” de Linux o cultos de rendimiento.
No son inútiles en todas las circunstancias. Simplemente rara vez arreglan tu problema en Ubuntu 24.04.
Tócalos solo cuando puedas explicar el modo de fallo y cómo medirás la mejora.

1) net.ipv4.tcp_tw_reuse — folclore TIME-WAIT

Reusar sockets en TIME-WAIT solía ser una “solución” común para la presión de puertos efímeros. Los stacks de red modernos y el comportamiento real de NAT/timestamps
hacen de esta una palanca arriesgada. La reutilización de conexiones a nivel de aplicación (keep-alives, pooling) es la solución madura.

2) net.ipv4.tcp_fin_timeout — parece útil, normalmente no lo es

Afinar el timeout FIN rara vez arregla la escasez de puertos porque el bulk suele ser TIME-WAIT, no FIN-WAIT.
Si te estás ahogando en sockets medio cerrados, pregunta por qué los peers no cierran limpiamente.

3) net.ipv4.tcp_syncookies — no es un tweak de rendimiento

Esto es un mecanismo de defensa contra SYN floods, no un potenciador de throughput.
Pónlo según la postura de seguridad, no porque lo viste en una lista de tuning.

4) net.ipv4.tcp_sack — por favor no lo alternes a la ligera

Deshabilitar SACK se ha sugerido durante eras específicas de vulnerabilidades. En la vida normal, SACK mejora la recuperación de pérdidas.
Cambiarlo puede dañar el rendimiento en redes imperfectas y rara vez está justificado para servidores generales.

5) net.core.netdev_max_backlog — la trampa de “simplemente haz la cola más grande”

Si los paquetes se acumulan porque la CPU no puede vaciar la cola, agrandarla puede aumentar latencia y jitter.
A veces la necesitas para absorber ráfagas; con frecuencia necesitas CPU, afinidad IRQ, tuning de RPS/RFS o simplemente menos paquetes.

6) net.ipv4.tcp_mtu_probing — un parche, no una base

La sonda de MTU puede ayudar en caminos con PMTUD roto. Si no estás diagnosticando agujeros negros, no hagas que tu stack adivine MTUs todo el día.

7) kernel.pid_max — rara vez es tu cuello de botella

Si agotas PIDs, algo más está muy mal: fork bombs, supervisores desbocados o cambio extremo de procesos.
Arregla el churn; no solo subas el techo.

8) vm.vfs_cache_pressure — el “control de cache” que la gente interpreta mal

En kernels modernos, la relación entre page cache, inode/dentry cache y reclaim es más matizada que este único número.
Si lo tocas sin medir reclaim y tasas de aciertos de cache, estás adivinando.

9) kernel.randomize_va_space — control de seguridad, no de velocidad

La aleatorización del espacio de direcciones afecta la dificultad de explotación. No es tu problema de latencia, y desactivarlo por rendimiento suele ser injustificable.

10) vm.overcommit_memory — malentendido y mal aplicado

El comportamiento de overcommit importa para cargas que hacen muchas asignaciones, pero cambiarlo “por rendimiento” es una forma clásica de convertir un pico de memoria recuperable en fallos de asignación inmediatos. Úsalo cuando entiendas los patrones de asignación y el manejo de fallos de la aplicación.

Broma #2: Configurar sysctls aleatorios de internet es como aceptar una solicitud de cambio en producción que dice “ajustes varios”; la única garantía es reuniones futuras.

Tareas prácticas: comandos, significado, decisiones (12+)

Estas son tareas reales que ejecuto en servidores Ubuntu antes y después de cambios de sysctl.
Cada tarea incluye: comando, qué significa la salida y la decisión que tomas a partir de ella.
Ejecútalas con un usuario con los privilegios adecuados.

Task 1: See what sysctls are actually set (and where)

cr0x@server:~$ sudo systemd-analyze cat-config systemd-sysctl.service
# /usr/lib/systemd/system/systemd-sysctl.service
...
# /usr/lib/sysctl.d/00-system.conf
net.ipv4.ip_forward = 0
...
# /etc/sysctl.d/99-local-tuning.conf
vm.swappiness = 10

Significado: Esto muestra qué archivos sysctl se aplican y en qué orden de precedencia.
Decisión: Si no puedes encontrar de dónde viene un valor, no lo cambies todavía. Arregla la higiene de la configuración primero.

Task 2: Check current values for the “five that matter”

cr0x@server:~$ sysctl fs.file-max net.core.somaxconn net.ipv4.ip_local_port_range vm.swappiness vm.dirty_ratio vm.dirty_background_ratio
fs.file-max = 9223372036854775807
net.core.somaxconn = 4096
net.ipv4.ip_local_port_range = 32768	60999
vm.swappiness = 60
vm.dirty_ratio = 20
vm.dirty_background_ratio = 10

Significado: Tienes una línea base. Nota: algunas distros ponen valores muy grandes para file-max; los límites por proceso siguen importando.
Decisión: Si swappiness es 60 en un nodo sensible a la latencia con swap habilitado, es candidato—después de confirmar que realmente estás usando swap.

Task 3: Validate file descriptor pressure system-wide

cr0x@server:~$ cat /proc/sys/fs/file-nr
12032	0	9223372036854775807

Significado: El primer número son manejadores de archivos asignados; el tercero es el máximo.
Decisión: Si lo asignado se acerca al máximo (o ves fallos de asignación), sube fs.file-max y audita procesos por fugas.

Task 4: Validate per-process file descriptor limits (systemd often wins)

cr0x@server:~$ systemctl show nginx --property=LimitNOFILE
LimitNOFILE=1024

Significado: Tu servicio solo puede abrir 1024 archivos/sockets, independientemente de fs.file-max.
Decisión: Si es un frontend ocupado, aumenta LimitNOFILE en el override de la unidad; el sysctl por sí solo no te salvará.

Task 5: Check listen queue drops and SYN backlog signals

cr0x@server:~$ netstat -s | sed -n '/listen queue/,/TCPBacklogDrop/p'
    120 times the listen queue of a socket overflowed
    120 SYNs to LISTEN sockets dropped

Significado: El kernel recibió conexiones más rápido de lo que la aplicación podía aceptarlas.
Decisión: Considera subir net.core.somaxconn y también perfilar el bucle de accept / TLS / saturación de workers.

Task 6: See how many ephemeral ports are actually in use

cr0x@server:~$ ss -s
Total: 32156 (kernel 0)
TCP:   27540 (estab 1320, closed 24511, orphaned 12, synrecv 0, timewait 23890/0), ports 0
Transport Total     IP        IPv6
RAW       0         0         0
UDP       316       280       36
TCP       3029      2510      519
INET      3345      3070      275
FRAG      0         0         0

Significado: Un gran conteo de TIME-WAIT indica churn; también puede indicar falta de keep-alives/pooling.
Decisión: Si el churn saliente es alto y ocurren fallos de connect, amplía ip_local_port_range y prioriza la reutilización de conexiones.

Task 7: Confirm you’re swapping (don’t guess)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            62Gi        41Gi       2.1Gi       1.2Gi        19Gi        18Gi
Swap:          8.0Gi       1.6Gi       6.4Gi

Significado: Swap está en uso. No siempre es malo, pero es sospechoso en nodos sensibles a la latencia.
Decisión: Si swap se usa y ves fallos mayores durante picos de latencia, baja vm.swappiness y/o añade memoria.

Task 8: Measure swap activity over time

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 1677720 2200000 120000 18400000   0   4     2   120  900 2400 12  6 78  4  0
 1  0 1677720 2195000 120000 18420000   0   0     0    80  850 2300 11  5 80  4  0
 3  1 1677720 2100000 120000 18500000  40  12   200   600 1200 4000 18  8 60 14  0

Significado: si/so (swap in/out) picos se correlacionan con dolor de latencia.
Decisión: Si hay swap-ins bajo carga, baja swappiness y arregla la presión de memoria. Si el swap es estable pero bajo, puede ser inofensivo.

Task 9: Inspect dirty/writeback behavior

cr0x@server:~$ egrep 'Dirty|Writeback|MemAvailable' /proc/meminfo
MemAvailable:   18342172 kB
Dirty:           914832 kB
Writeback:        26304 kB

Significado: Los datos sucios son actualmente ~900MB. Eso puede estar bien o ser alarmante dependiendo de RAM y carga.
Decisión: Si Dirty crece a multi-GB y los picos de latencia se alinean con tormentas de flush, ajusta los dirty ratios (o bytes) para suavizar el writeback.

Task 10: Correlate I/O latency quickly

cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0-xx-generic (server) 	12/30/2025 	_x86_64_	(16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.40    0.00    5.80   18.20    0.00   63.60

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1          40.0   1024.0     0.0   0.00    2.10    25.6     900.0   32000.0    10.0   1.10   45.00    35.6   41.2   98.0

Significado: Alto w_await, alto aqu-sz y utilización cercana al 100% indican que el dispositivo está saturado.
Decisión: No “tunees el kernel” primero. Arregla el throughput/latencia de almacenamiento o reduce la presión de escritura; el tuning de dirty solo puede redistribuir el dolor.

Task 11: Check conntrack saturation (classic NAT/gateway failure mode)

cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 252144
net.netfilter.nf_conntrack_max = 262144

Significado: Estás al 4% del máximo de conntrack. Eso es una luz roja parpadeante.
Decisión: Si esto es un nodo stateful firewall/NAT/load balancer, aumenta nf_conntrack_max y asegúrate de tener margen de memoria. También investiga el churn de conexiones.

Task 12: Verify conntrack drops in kernel logs

cr0x@server:~$ sudo dmesg -T | tail -n 8
[Mon Dec 30 10:14:02 2025] nf_conntrack: table full, dropping packet
[Mon Dec 30 10:14:02 2025] nf_conntrack: table full, dropping packet

Significado: Se están descartando paquetes porque conntrack está lleno. Los usuarios experimentarán timeouts y reintentos.
Decisión: Aumenta la capacidad de conntrack y reduce el churn. Esto no es un “tal vez”; es un cuello de botella confirmado.

Task 13: Check TCP retransmits and drops (don’t blame sysctls for a bad link)

cr0x@server:~$ netstat -s | sed -n '/Tcp:/,/Ip:/p'
Tcp:
    923849 active connection openings
    12 failed connection attempts
    210938 segments retransmitted
    41 resets sent

Significado: Las retransmisiones son señal de pérdida de paquetes, congestión o pobre encolamiento.
Decisión: Si las retransmisiones suben con problemas de rendimiento, investiga la ruta de red, descartes de NIC, disciplinas de cola y congestión—no sysctls TCP al azar.

Task 14: Apply a sysctl change temporarily (for validation only)

cr0x@server:~$ sudo sysctl -w net.core.somaxconn=8192
net.core.somaxconn = 8192

Significado: El cambio se aplica de inmediato, no es persistente al reinicio.
Decisión: Usa esto para validación A/B durante una ventana de incidente, luego hazlo persistente correctamente si ayudó.

Task 15: Make changes persistent and reload them

cr0x@server:~$ sudo tee /etc/sysctl.d/99-local-tuning.conf >/dev/null <<'EOF'
net.core.somaxconn = 8192
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
EOF
cr0x@server:~$ sudo sysctl --system
* Applying /etc/sysctl.d/99-local-tuning.conf ...
net.core.somaxconn = 8192
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

Significado: Los valores ahora son persistentes y se aplicaron en el orden correcto.
Decisión: Registra el ticket de cambio, métricas antes/después y el plan de rollback (eliminar el archivo o revertir valores).

Tres mini-historias corporativas desde el campo

Mini-historia #1: El incidente causado por una suposición equivocada (edición conntrack)

Una compañía mediana ejecutaba un conjunto de nodos “edge”: HAProxy, NAT y algo de enrutamiento L7 ligero. El equipo asumía que los nodos eran stateless porque
“solo reenvían”. Esa suposición era cómoda, popular y equivocada.

Se lanzó una integración con un partner. El tráfico no solo creció; se volvió más ruidoso. Un storm de reintentos golpeó tras un breve contratiempo upstream, convirtiendo una curva de tráfico ordenada
en una erupción. Los nodos edge empezaron a hacer timeouts en nuevas conexiones. Nada obvio en las gráficas de CPU. La memoria parecía estable. El almacenamiento era irrelevante.

Alguien subió net.core.somaxconn. Otra persona culpó a TLS. La pista real estaba en el log del kernel: conntrack table full, dropping packets.
Esos nodos hacían NAT, así que el estado de conntrack no era opcional; era el producto.

La solución fue aburrida: aumentar net.netfilter.nf_conntrack_max y dimensionarlo según la memoria disponible, luego reducir el churn arreglando la reutilización de conexiones en clientes
y añadiendo circuit breakers para evitar que los reintentos se convirtieran en auto-daño. Después, el equipo actualizó sus docs de diseño: “edge stateful”.
Esa sola frase evitó futuras reuniones de “pero es stateless”.

Mini-historia #2: La optimización que salió mal (dirty ratios e ilusión de velocidad)

Otra organización tenía un pipeline de logging con un buffer en disco local. Bajo ingest pesado, el buffer ocasionalmente se quedaba atrás. Un ingeniero notó
que los discos “no estaban ocupados” la mayor parte del tiempo y quiso agrupar escrituras por throughput. Aumentó vm.dirty_ratio significativamente,
esperando menos flushes y mejor eficiencia de disco.

El throughput lució genial—por un rato. Luego el on-call empezó a ver paradas periódicas de varios segundos en el servicio de ingestión.
No una desaceleración suave. Paradas totales. Las alertas downstream saltaron porque la ingestión se volvió en ráfagas, lo que hizo el indexing en ráfagas y las consultas en ráfagas.
Fue como ver un pequeño temblor desencadenar una cadena de estanterías cayendo.

El kernel había acumulado una gran pila de páginas sucias. Cuando la presión de writeback finalmente entró, el sistema pagó la deuda de golpe.
Algunos hilos que necesitaban alocar memoria o escribir metadatos pequeños quedaron detrás de la tormenta de writeback. El gráfico de “discos no ocupados” era una mentira:
estaban inactivos hasta que dejaron de estarlo, y luego pasaron al 100% con alta await.

Revertir el cambio estabilizó la latencia de inmediato. La solución a largo plazo fue más matizada: ratios sucios moderados (o umbrales en bytes),
mejor batching dentro de la aplicación donde podía controlarse y más ancho de banda de disco. La revisión del incidente dejó una lección clara:
el buffering del kernel puede suavizar; también puede esconder riesgo hasta que detona.

Mini-historia #3: La práctica aburrida pero correcta que salvó el día (límites y observabilidad)

Un equipo de servicios financieros operaba una API de alto tráfico con SLOs estrictos. Su postura de tuning era casi irritante en su disciplina:
cada cambio de sysctl requería una hipótesis, un enlace a un dashboard y un procedimiento de rollback. Mantenían un pequeño archivo versionado en
/etc/sysctl.d/ con comentarios explicando cada desviación de los valores por defecto.

Una tarde, un despliegue introdujo una fuga sutil de sockets bajo una ruta de error específica. Los descriptores de archivos subieron lentamente.
En un equipo menos disciplinado, el primer síntoma habría sido un outage y una reacción histérica de “aumentad límites”.

En cambio, tenían dos salvaguardas. Primero: un dashboard que rastreaba /proc/sys/fs/file-nr y contadores por proceso de FDs abiertos.
Segundo: margen sano en LimitNOFILE y en el file-max del kernel, así que la fuga tuvo espacio para ser detectada y revertida antes de una falla dura.

Aun así tuvieron que arreglar el bug. Pero el trabajo “aburrido”—sysctls documentados, líneas base medidas y límites dimensionados para la realidad—convirtió un posible incidente
en un rollback rutinario. Ese es el juego.

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

1) “Time-outs de conexión aleatorios durante picos de tráfico”

Síntoma: Los clientes ven fallos de conexión intermitentes durante ráfagas; la CPU no está al máximo.

Causa raíz: Desbordamiento del backlog de escucha (somaxconn demasiado bajo) o bucle de accept demasiado lento.

Solución: Aumenta net.core.somaxconn a 4096–8192 y confirma con los contadores de cola de listen en netstat -s; perfila accept/TLS y saturación de workers.

2) “Todo se congela por segundos, luego se recupera”

Síntoma: Stalls periódicos; utilización de disco al 100%; p99 en sierra.

Causa raíz: Acumulación de páginas sucias y tormentas de writeback (dirty ratios demasiado altos para el dispositivo).

Solución: Baja vm.dirty_background_ratio y vm.dirty_ratio (o usa límites en bytes), luego confirma la reducción de la oscilación Dirty/Writeback y la mejora en I/O await.

3) “Llamadas salientes fallan bajo carga con EADDRNOTAVAIL”

Síntoma: Errores de conexión en el cliente, a menudo durante reintentos o ráfagas.

Causa raíz: Agotamiento de puertos efímeros o excesivo TIME-WAIT debido al churn de conexiones.

Solución: Amplía net.ipv4.ip_local_port_range, reduce el churn mediante keep-alives/pooling y monitoriza TIME-WAIT con ss -s.

4) “nf_conntrack: table full” y timeouts visibles por usuarios

Síntoma: Logs del kernel muestran drops de conntrack; nodos NAT/firewall hacen timeout.

Causa raíz: nf_conntrack_max demasiado bajo para el churn y la concurrencia de conexiones.

Solución: Aumenta net.netfilter.nf_conntrack_max, asegura margen de memoria y reduce churn (timeouts de inactividad, reutiliza conexiones, evita tormentas de reintentos).

5) “Pusimos swappiness a 1 y sigue lento”

Síntoma: Persisten problemas de latencia; la memoria está ajustada; el swap puede seguir usándose.

Causa raíz: Realmente estás limitado de memoria; swappiness no es capacidad. Además, la carga podría estar limitada por I/O o CPU.

Solución: Mide fallos mayores y swap-ins; si la presión de memoria es real, añade RAM o reduce uso de memoria. Luego ajusta swappiness como política, no como rescate.

6) “Aumentamos todos los buffers TCP y empeoró”

Síntoma: La latencia aumentó; el uso de memoria subió; sin ganancia de throughput.

Causa raíz: Los buffers aumentaron el encolamiento y la presión de memoria; el cuello de botella no era el tamaño de ventana TCP.

Solución: Revierte los cambios de buffers, valida retransmisiones/descartes, comprueba CPU y encolamiento de NIC; tunéa solo con un problema BDP probado.

7) “Demasiados archivos abiertos” a pesar de un fs.file-max enorme

Síntoma: Errores de la aplicación muestran agotamiento de FDs; file-max del kernel parece masivo.

Causa raíz: Límites por proceso (systemd LimitNOFILE) son bajos.

Solución: Aumenta LimitNOFILE de la unidad y verifica con systemctl show y /proc/<pid>/limits.

Listas de verificación / plan paso a paso

Checklist A: Flujo seguro de tuning de sysctl (producción)

  1. Escribe la hipótesis: “Estamos perdiendo conexiones entrantes por desbordamiento de backlog; aumentar somaxconn reducirá drops y mejorará p99 de conexión.”
  2. Elige 2–3 métricas: una señal del kernel (p. ej., overflow de listen queue), un síntoma usuario (latencia p99), una métrica de recurso (CPU, memoria, I/O await).
  3. Captura la línea base: ejecuta las tareas relevantes arriba; guarda la salida con marcas de tiempo.
  4. Aplica cambio temporalmente: sysctl -w durante una ventana controlada, o en un nodo canario.
  5. Observa: ¿mejoró la señal del kernel y la métrica de usuario sin daño colateral?
  6. Hazlo persistente: escribe en /etc/sysctl.d/99-local-tuning.conf, luego sysctl --system.
  7. Documenta: qué cambió, por qué y cómo es el rollback.
  8. Revalida tras reboot: confirma que el valor persiste y el comportamiento es el mismo.

Checklist B: Perfil mínimo “cinco sysctls” para roles de servidor comunes

Ingreso HTTP ocupado / reverse proxy

  • net.core.somaxconn: considera 4096–8192 si ves overflow de listen.
  • fs.file-max: asegura margen suficiente; también aumenta LimitNOFILE de la unidad.
  • net.ipv4.ip_local_port_range: relevante si también actúa como cliente saliente intensivo.
  • vm.swappiness: reduce si los swap-ins causan picos de latencia.
  • vm.dirty_*: solo si el logging/buffering local causa stalls.

Nodo de base de datos (sensitivo a latencia, con muchas escrituras)

  • vm.swappiness: a menudo 1–10, pero solo con impacto de swapping confirmado.
  • vm.dirty_ratio/vm.dirty_background_ratio: ajusta para evitar acantilados de writeback; mide I/O await y dirty/writeback.
  • fs.file-max: asegura margen; las DBs pueden usar muchos archivos y sockets.
  • net.core.somaxconn: rara vez factor limitante a menos que también sirva ráfagas TCP.
  • ip_local_port_range: mayormente irrelevante salvo que la DB inicie muchas conexiones salientes.

Gateway NAT / firewall / nodo Kubernetes con egress intenso

  • net.netfilter.nf_conntrack_max: no está en la lista “cinco” anterior, pero para este rol es de primer orden. Dimensiónalo deliberadamente.
  • ip_local_port_range: relevante para clientes con muchas conexiones o patrones de egress compartido.
  • fs.file-max: relevante para proxies y agentes.

Checklist C: Plan de rollback que realmente funcione

  1. Mantén una copia de los valores previos al cambio: sysctl -a es ruidoso; al menos registra las claves que tocaste.
  2. Para cambios persistentes, revierte el archivo en /etc/sysctl.d/, luego ejecuta sysctl --system.
  3. Si debes revertir al instante, usa sysctl -w key=value para las pocas claves tocadas, luego revierte el archivo tras el incidente.
  4. Confirma con sysctl key que el rollback se aplicó.

FAQ

1) ¿Debería tunear sysctls en Ubuntu 24.04?

Solo cuando tengas un cuello de botella medido y un modo de fallo claro. Los valores por defecto son adecuados para uso general, no para cada caso de borde de alta carga.

2) ¿Por qué no aplicar un “perfil de rendimiento sysctl” de internet?

Porque suele ser una mezcla de consejos desactualizados, toggles de seguridad y ajustes específicos de carga presentados como verdad universal.
No arreglarás nada y complicarás la depuración.

3) ¿Es vm.swappiness=1 siempre lo mejor para servidores?

No. A menudo es una buena política para nodos sensibles a la latencia con swap habilitado como freno de emergencia, pero no resolverá la falta de memoria.
Si estás swap-eando bajo carga sostenida, necesitas memoria o menos carga.

4) ¿Cuál es la relación real entre fs.file-max y ulimit -n?

fs.file-max es un techo a nivel kernel sobre manejadores de archivos. ulimit -n (y systemd LimitNOFILE) limita archivos abiertos por proceso.
Puedes tener un límite global enorme y aun así fallar porque un servicio está capado en 1024.

5) Aumenté net.core.somaxconn. ¿Por qué sigo viendo drops de conexión?

Porque el backlog no es la única cola: el bucle accept de tu aplicación puede ser lento, el comportamiento del backlog de SYN importa, y la saturación de CPU/IRQ puede descartar paquetes antes.
Mide dónde ocurre el drop y si el proceso realmente está aceptando rápido.

6) ¿Debo tunear buffers TCP rmem/wmem en Ubuntu 24.04?

Solo si has demostrado un problema de producto ancho de banda-retardo (bandwidth-delay product) (enlaces de alta latencia y alto ancho de banda donde el throughput está limitado por la ventana)
y el autotuning no es suficiente. De lo contrario solo aumentarás uso de memoria y latencia por encolamiento.

7) ¿Es seguro tunear vm.dirty_ratio?

Es seguro cuando lo validas: observa Dirty/Writeback, I/O await y la latencia tail de la aplicación.
Es inseguro cuando subes los ratios para perseguir throughput; eso puede manufacturar tormentas de writeback.

8) ¿Cómo persisto cambios sysctl “a la manera de Ubuntu 24.04”?

Ponlos en /etc/sysctl.d/99-local-tuning.conf (o similar), luego ejecuta sysctl --system.
Evita cambios ad-hoc que desaparecen tras reiniciar.

9) ¿Los contenedores ignoran los sysctls del host?

Algunos sysctls son namespaced y pueden establecerse por contenedor; otros son globales para el kernel del host.
En la práctica: el tuning a nivel host puede afectar a todas las cargas. Trátalo como un cambio de infraestructura compartida.

10) Si solo puedo cambiar una cosa durante un incidente, ¿cuál es la apuesta más segura?

Depende, pero el movimiento operacional más seguro suele no ser un sysctl: reduce carga (shed traffic), añade capacidad o revierte un release.
Para sysctls, aumentar somaxconn o ampliar el rango de puertos puede ser de bajo riesgo si tienes evidencias que lo justifican.

Siguientes pasos que puedes hacer hoy

Si quieres tuning del kernel que mejore la fiabilidad en vez de producir folclore, sigue esta secuencia:

  1. Elige un rol de nodo (ingreso, base de datos, NAT, worker). No tunees “la flota” por sensaciones.
  2. Ejecuta la sección de tareas y guarda las salidas como tu línea base.
  3. Identifica el cuello de botella dominante usando la guía rápida de diagnóstico—CPU, memoria, disco o red.
  4. Cambia solo uno de los cinco sysctls que coincida con el cuello de botella, y solo si tienes señales confirmantes.
  5. Hazlo persistente vía sysctl.d y documenta la razón en comentarios, no en conocimiento tribal.
  6. Revisa después de un reinicio y un ciclo de tráfico. Si no puedes demostrar mejora, revierte y sigue con otra cosa.

El mejor tuning es el que puedes explicar seis meses después sin pedir disculpas. El segundo mejor es el que no necesitaste porque dimensionaste el sistema correctamente.

← Anterior
Preparación del dominio de correo: plan práctico para evitar marcas de spam instantáneas
Siguiente →
MySQL vs MariaDB en un VPS de 2GB: perfiles de ajuste que no se caen

Deja un comentario