Ajuste de parámetros del kernel Linux para servidores web de alto tráfico
Introducción
La optimización de un servidor web de alto tráfico trasciende la configuración del software de aplicación (Nginx, Apache, PHP-FPM) y la base de datos. El núcleo del sistema operativo, el kernel de Linux, actúa como el árbitro final de todos los recursos del hardware. Su configuración por defecto está pensada para un uso generalista y de escritorio, no para manejar decenas de miles de conexiones concurrentes, picos de tráfico repentinos y una latencia mínima.
Ignorar los parámetros del kernel (sysctl) es el error más común en servidores mal optimizados. Un kernel mal ajustado puede provocar desde timeouts inexplicables y colas de conexión desbordadas, hasta un agotamiento completo de la memoria del sistema por culpa de búferes mal dimensionados. Este artículo detalla, desde la perspectiva de un SysAdmin y experto en rendimiento, los ajustes críticos del kernel necesarios para que un servidor web pueda soportar cargas extremas de manera estable y eficiente.
Cada parámetro que modificaremos tiene un impacto directo en cómo el kernel maneja la red, la memoria y los procesos. No se trata de aplicar recetas mágicas, sino de entender la fisiología del sistema para tomar decisiones informadas.
Fundamentos del Ajuste: El Archivo /etc/sysctl.conf
La interfaz principal para modificar parámetros del kernel en tiempo de ejecución y de forma persistente es el archivo /etc/sysctl.conf (o archivos dentro de /etc/sysctl.d/). Utilizaremos sysctl -w para cambios inmediatos y editaremos el archivo para hacerlos permanentes.
Advertencia: Modificar parámetros del kernel puede desestabilizar el sistema. Realiza siempre una copia de seguridad del archivo de configuración original (
cp /etc/sysctl.conf /etc/sysctl.conf.backup) y prueba los cambios en un entorno de staging antes de aplicarlos a producción.
Flujo de trabajo para aplicar cambios
- Verificar valor actual:
sysctl <nombre.del.parametro> - Aplicar cambio temporal:
sysctl -w <nombre.del.parametro>=<nuevo_valor> - Hacer cambio permanente: Editar
/etc/sysctl.confo crear un archivo, ej./etc/sysctl.d/99-sysprovider-web.conf. - Recargar configuración:
sysctl -p(osysctl --systemen systemd).
Ajustes de Red (net.*): El Corazón de la Conectividad
El subsistema de red es el más crítico para un servidor web. Los parámetros por defecto están diseñados para clientes ligeros, no para un servidor que debe aceptar y gestionar miles de conexiones simultáneas.
net.core.somaxconn y net.ipv4.tcp_max_syn_backlog: La Profundidad de la Cola de Espera
Cuando una aplicación (como Nginx) acepta conexiones, el kernel las encola. Si la cola se llena, las nuevas conexiones son rechazadas (errores connection refused).
net.core.somaxconn: Define el límite máximo de la cola de "escucha" para sockets. Es el valor que las aplicaciones usan como argumento en la llamada al sistemalisten(). Por defecto es 128, un valor ridículamente bajo para un servidor de alto tráfico.net.ipv4.tcp_max_syn_backlog: Es la cola de conexiones que aún no han completado el three-way handshake (estado SYN_RECV). Un valor bajo aquí provoca que se descarten paquetes SYN bajo alta carga, causando reintentos por parte del cliente y aumentando la latencia.
¿Por qué aumentarlos? Para permitir que el servidor pueda "respirar" bajo picos de tráfico. Si tu servidor recibe 10,000 conexiones por segundo, una cola de 128 es un cuello de botella instantáneo.
# Aumentar la cola de escucha de la aplicación
sysctl -w net.core.somaxconn=65535
# Aumentar la cola de handshake incompleto
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
net.ipv4.tcp_tw_reuse y net.ipv4.tcp_fin_timeout: Gestión del Estado TIME_WAIT
Este es uno de los puntos más delicados y debatidos. Cuando un TCP connection se cierra, el lado que inicia el cierre entra en estado TIME_WAIT durante un periodo (2 * MSL, generalmente 60 segundos). En un servidor web con muchas conexiones cortas (HTTP/1.0 o HTTP/1.1 con keep-alive mal configurado), se pueden acumular decenas de miles de sockets en TIME_WAIT, agotando la memoria y los puertos efímeros.
net.ipv4.tcp_tw_reuse: Permite reutilizar sockets en estadoTIME_WAITpara nuevas conexiones salientes. Es seguro activarlo en un servidor web que actúa como cliente (ej. al conectarse a un backend, base de datos o proxy inverso).net.ipv4.tcp_fin_timeout: Reduce el tiempo que un socket huérfano (no asociado a un proceso) permanece en estadoFIN-WAIT-2. Un valor más bajo libera recursos más rápido.
Nota importante sobre
tcp_tw_recycle: El parámetronet.ipv4.tcp_tcp_tw_recycleha sido eliminado en kernels modernos (>= 4.12) por ser problemático con NAT y firewalls. NO LO USES. Causa problemas de conectividad intermitentes.
# Permitir reutilización de sockets TIME_WAIT para conexiones salientes
sysctl -w net.ipv4.tcp_tw_reuse=1
# Reducir el tiempo en FIN-WAIT-2 (por defecto 60 segundos)
sysctl -w net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_keepalive_*: Manteniendo Conexiones Vivas
Para conexiones largas (WebSockets, HTTP/2, conexiones a bases de datos), el servidor debe detectar si el otro extremo sigue activo. Los parámetros tcp_keepalive_time, tcp_keepalive_intvl y tcp_keepalive_probes controlan este mecanismo.
tcp_keepalive_time(por defecto 7200s): Tiempo de inactividad antes de enviar el primer probe.tcp_keepalive_intvl(por defecto 75s): Intervalo entre probes.tcp_keepalive_probes(por defecto 9): Número de probes antes de declarar la conexión muerta.
Para un servidor web con muchas conexiones, esperar 2 horas para detectar una conexión muerta es ineficiente. Reducir estos valores libera recursos más rápido.
# Reducir el tiempo de inactividad a 300 segundos (5 minutos)
sysctl -w net.ipv4.tcp_keepalive_time=300
# Enviar probes cada 30 segundos
sysctl -w net.ipv4.tcp_keepalive_intvl=30
# Solo 3 probes antes de morir (total 300 + 3*30 = 390s para detectar muerte)
sysctl -w net.ipv4.tcp_keepalive_probes=3
Búferes de Red (net.core.rmem_max, net.core.wmem_max, net.ipv4.tcp_rmem, net.ipv4.tcp_wmem)
Los búferes de socket determinan cuántos datos puede almacenar el kernel para una conexión antes de que la aplicación los procese. Búferes pequeños causan pérdida de paquetes y baja throughput. Búferes demasiado grandes desperdician memoria.
net.core.rmem_maxynet.core.wmem_max: Son los límites globales máximos para los búferes de recepción y envío.net.ipv4.tcp_rmemynet.ipv4.tcp_wmem: Son vectores de tres valores:min default max. El kernel ajusta automáticamente el tamaño del búfer entreminymaxbasándose en la congestión de la red.
Para servidores web con mucho tráfico, aumentar estos límites mejora el rendimiento en conexiones de alta latencia o gran ancho de banda.
# Aumentar el máximo global de búferes
sysctl -w net.core.rmem_max=134217728 # 128 MB
sysctl -w net.core.wmem_max=134217728 # 128 MB
# Ajustar los búferes TCP: min, default, max (en bytes)
sysctl -w net.ipv4.tcp_rmem="4096 87380 134217728"
sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728"
4096(min): Suficiente para conexiones de baja latencia.87380(default): Valor por defecto de Linux, buen punto de partida.134217728(max): Permite que el búfer crezca hasta 128 MB para conexiones de alto rendimiento.
Ajustes de Memoria (vm.) y Sistema de Archivos (fs.)
No solo la red importa. La gestión de la memoria y los descriptores de archivo son igualmente cruciales.
fs.file-max y fs.nr_open: El Límite de Descriptores de Archivo
Todo en Linux es un archivo: sockets, conexiones, archivos abiertos. Cada conexión HTTP consume al menos un descriptor de archivo. Si el límite es demasiado bajo, el servidor web no podrá aceptar nuevas conexiones, incluso si tiene CPU y RAM libres.
fs.file-max: Es el límite global del sistema para el número de descriptores de archivo abiertos.fs.nr_open: Es el límite por proceso (generalmente no se modifica, pero es el tope paraulimit -n).
Calcula este valor en función de tu carga máxima esperada. Para un servidor con 64 GB de RAM y 50,000 conexiones concurrentes, un valor de 1000000 (1 millón) es razonable.
# Aumentar el límite global de descriptores de archivo
sysctl -w fs.file-max=1000000
# Verificar el límite actual de tu proceso (Nginx)
# cat /proc/<pid_de_nginx>/limits
Importante: También debes ajustar los límites de
systemdoinitpara el servicio de tu servidor web. En/etc/systemd/system/nginx.service.d/override.conf, añadeLimitNOFILE=1000000.
vm.swappiness: La Pereza de la Memoria Swap
vm.swappiness controla la tendencia del kernel a mover páginas de memoria activa a la partición swap. El valor va de 0 a 100. Un valor alto (por defecto 60) hace que el kernel swapée incluso cuando hay mucha RAM libre, lo cual es desastroso para un servidor web, ya que la latencia del disco es órdenes de magnitud mayor que la de la RAM.
Para un servidor web, queremos que la RAM se use al máximo para almacenar en caché archivos y datos de la aplicación. La swap debe ser un recurso de emergencia, no un mecanismo de gestión de memoria activa.
# Reducir drásticamente la tendencia a swappear
sysctl -w vm.swappiness=10
Un valor de 1 o 0 es común en servidores de bases de datos. Para servidores web, 10 es un buen equilibrio: el kernel solo swappeará si la presión de memoria es muy alta.
vm.vfs_cache_pressure: Gestión de Caché de VFS
El kernel almacena en caché metadatos de inodos y entradas de directorio (dentry) para acelerar el acceso a archivos. vm.vfs_cache_pressure controla la tendencia del kernel a reclamar esta caché cuando hay presión de memoria.
- Valor por defecto (100): El kernel reclamará la caché de VFS al mismo ritmo que la caché de página.
- Valor bajo (por ejemplo, 50): El kernel es más reacio a reclamar la caché de VFS, lo que puede mejorar el rendimiento en servidores que abren y cierran muchos archivos (como un servidor web sirviendo archivos estáticos).
- Valor alto (por ejemplo, 200): El kernel reclamará la caché de VFS de forma agresiva.
# Reducir la presión de reclamación de la caché de VFS
sysctl -w vm.vfs_cache_pressure=50
Parámetros de Rendimiento Adicionales
net.core.netdev_max_backlog: Cola de Entrada de la Interfaz de Red
Cuando el kernel recibe paquetes de la tarjeta de red, los coloca en una cola antes de que el protocolo TCP/IP los procese. Si esta cola se llena, los paquetes se descartan.
# Aumentar la cola de entrada de la interfaz de red
sysctl -w net.core.netdev_max_backlog=50000
net.ipv4.tcp_syncookies: Protección contra SYN Flood
tcp_syncookies es un mecanismo de seguridad que protege contra ataques SYN Flood. Cuando la cola SYN está llena, en lugar de descartar paquetes, el kernel envía una "cookie" SYN-ACK especial. Si recibe un ACK válido, la conexión se establece.
# Mantener habilitado (está por defecto)
sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.ip_local_port_range: Rango de Puertos Efímeros
Cuando tu servidor web actúa como cliente (conexiones a backend, base de datos, APIs externas), necesita un puerto local efímero para cada conexión saliente. El rango por defecto (32768 60999) solo proporciona aproximadamente 28,000 puertos. Si tienes muchas conexiones salientes simultáneas, puedes agotarlos.
# Ampliar el rango de puertos efímeros
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
Plantilla de Configuración Completa (sysctl)
A continuación, una plantilla consolidada. Guárdala como /etc/sysctl.d/99-sysprovider-web.conf y cárgala con sysctl --system.
# /etc/sysctl.d/99-sysprovider-web.conf
# Ajustes de kernel para servidores web de alto tráfico
# --- Ajustes de Red ---
# Colas de conexión
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Gestión de TIME_WAIT y conexiones cortas
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# Keepalive
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# Búferes de red
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
# Cola de entrada de interfaz
net.core.netdev_max_backlog = 50000
# Protección SYN Flood
net.ipv4.tcp_syncookies = 1
# Rango de puertos efímeros
net.ipv4.ip_local_port_range = 1024 65535
# --- Ajustes de Memoria y Sistema de Archivos ---
# Descriptores de archivo
fs.file-max = 1000000
# Gestión de swap
vm.swappiness = 10
# Caché de VFS
vm.vfs_cache_pressure = 50
Verificación y Monitorización Post-Ajuste
Aplicar los cambios es solo el primer paso. Debes verificar que realmente están teniendo el efecto deseado.
Comprobación de parámetros activos
# Verificar un parámetro específico
sysctl net.core.somaxconn
# Listar todos los parámetros de red no predeterminados
sysctl -a 2>/dev/null | grep -E "^net\." | head -20
