Los valores por defecto del kernel Linux funcionan bien para la mayoría de servidores, pero un servidor web con muchos clientes concurrentes puede toparse con colas de conexión que se desbordan, límites de descriptores de archivo o un control de congestión poco eficiente en enlaces con pérdidas. En este tutorial medirás primero el estado actual, aplicarás un conjunto pequeño y justificado de parámetros sysctl en Ubuntu 24.04, ajustarás Nginx para que aproveche los nuevos límites y comprobarás el resultado con contadores del kernel y una prueba de carga.
La idea no es copiar cientos de parámetros, sino cambiar solo los que resuelven un problema medible y dejar el resto en sus valores por defecto, que en el kernel 6.8 de Ubuntu 24.04 ya son razonables.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Nginx instalado y sirviendo un sitio (
sudo apt install nginx). Los ajustes de kernel sirven igual para Apache, Caddy o HAProxy; solo cambia la sección de configuración de la aplicación. - Opcional, para el paso 5: una segunda máquina desde la que lanzar la prueba de carga. Lanzarla desde el propio servidor mide el servidor compitiendo consigo mismo.
Paso 1: Medir el estado actual
Antes de cambiar nada, anota los valores actuales y comprueba si hay síntomas reales. Consulta los parámetros que vas a tocar:
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.core.netdev_max_backlog net.ipv4.tcp_congestion_control net.ipv4.ip_local_port_range
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 1024
net.core.netdev_max_backlog = 1000
net.ipv4.tcp_congestion_control = cubic
net.ipv4.ip_local_port_range = 32768 60999
El valor de tcp_max_syn_backlog depende de la memoria del servidor, así que puede ser distinto en el tuyo.
Ahora revisa los dos contadores que indican que la cola de conexiones pendientes se ha llenado y el kernel ha descartado clientes. La herramienta nstat forma parte del paquete iproute2, instalado por defecto:
nstat -az TcpExtListenOverflows TcpExtListenDrops
#kernel
TcpExtListenOverflows 0 0.0
TcpExtListenDrops 0 0.0
Si estos contadores crecen durante los picos de tráfico, la cola de aceptación es demasiado pequeña. Mira también la cola de los sockets en escucha con ss. En un socket en estado LISTEN, Send-Q es el tamaño máximo de la cola (el backlog) y Recv-Q las conexiones que esperan a que la aplicación las acepte:
ss -ltn 'sport = :80'
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
Ese 511 es el backlog que Nginx pide por defecto en Linux, aunque el kernel permita hasta 4096. Lo corregirás en el paso 4.
Por último, un resumen de los estados TCP te dirá si tienes miles de conexiones en TIME-WAIT:
ss -s
Paso 2: Crear el archivo de ajustes sysctl
Los cambios con sysctl -w se pierden al reiniciar. Guarda los ajustes en un archivo propio dentro de /etc/sysctl.d/, que se carga en cada arranque:
sudo nano /etc/sysctl.d/99-servidor-web.conf
Añade el siguiente contenido:
# Cola de conexiones
net.core.somaxconn = 8192
net.ipv4.tcp_max_syn_backlog = 8192
net.core.netdev_max_backlog = 16384
# Control de congestión BBR con la cola fq
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Buffers TCP: tamaño máximo de 16 MB por socket
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 131072 16777216
net.ipv4.tcp_wmem = 4096 16384 16777216
# Conexiones keepalive y conexiones salientes
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.ip_local_port_range = 10240 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
Qué hace cada bloque:
- Cola de conexiones:
somaxconnes el máximo que el kernel acepta para la cola de conexiones establecidas de cada socket en escucha, ytcp_max_syn_backlogel de conexiones a medio abrir (SYN recibido).netdev_max_backloges la cola de paquetes que llegan por la interfaz antes de que el kernel los procese. Subirlos solo sirve si la aplicación también pide un backlog mayor, como verás en el paso 4. - BBR y fq: BBR es un algoritmo de control de congestión que estima el ancho de banda y la latencia del camino en lugar de reaccionar solo a la pérdida de paquetes. Suele mejorar la velocidad de descarga para clientes lejanos o en redes móviles.
fqes la disciplina de colas recomendada para acompañarlo. - Buffers TCP: los tres valores de
tcp_rmemytcp_wmemson mínimo, valor inicial y máximo. El kernel ajusta cada socket de forma automática dentro de ese rango, así que subir el máximo no reserva memoria por adelantado. Un máximo de 16 MB permite aprovechar enlaces rápidos con latencia alta. - Keepalive y salientes:
tcp_slow_start_after_idle = 0evita que una conexión keepalive inactiva vuelva a empezar con una ventana pequeña.ip_local_port_rangeamplía los puertos de origen disponibles para las conexiones que el servidor abre hacia backends (proxy inverso, PHP-FPM por TCP, bases de datos). Empieza en 10240 para no chocar con servicios que escuchan en puertos bajos.tcp_tw_reuse = 1permite reutilizar sockets enTIME-WAITpara nuevas conexiones salientes, ytcp_fin_timeoutreduce el tiempo enFIN-WAIT-2.
Notano uses
net.ipv4.tcp_tw_recycle. Rompía conexiones de clientes detrás de NAT y se eliminó del kernel en la versión 4.12, así que en Ubuntu 24.04sysctldevolverá un error si lo incluyes.
Antes de aplicar el archivo, confirma que el kernel tiene BBR disponible:
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic bbr
Para que el módulo se cargue también en cada arranque, añádelo a /etc/modules-load.d/:
echo tcp_bbr | sudo tee /etc/modules-load.d/bbr.conf
Paso 3: Aplicar y verificar los cambios
Carga todos los archivos de /etc/sysctl.d/, igual que ocurrirá en el próximo arranque:
sudo sysctl --system
La salida lista cada archivo leído y cada valor aplicado. Si algún parámetro no existe o tiene un formato incorrecto, verás una línea con sysctl: cannot stat o Invalid argument: corrígela antes de seguir.
Comprueba los valores clave:
sysctl net.core.somaxconn net.ipv4.tcp_congestion_control net.core.default_qdisc
net.core.somaxconn = 8192
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
Las conexiones nuevas usarán BBR de inmediato. Puedes verlo en cualquier conexión activa, por ejemplo tu propia sesión SSH después de reconectar:
ss -ti state established '( sport = :22 )' | grep -o 'bbr'
bbr
La cola fq se aplica a las interfaces al crearse, así que la interfaz principal seguirá con la cola anterior hasta el próximo reinicio. Si quieres activarla ya sin reiniciar, sustituye eth0 por el nombre de tu interfaz (ip -br link lo muestra):
sudo tc qdisc replace dev eth0 root fq
tc qdisc show dev eth0
qdisc fq 8001: root refcnt 2 limit 10000p flow_limit 100p ...
Paso 4: Ajustar Nginx a los nuevos límites
El kernel limita el backlog, pero es la aplicación quien lo solicita al abrir el socket. Nginx pide 511 por defecto, así que hay que indicarle el nuevo valor en la directiva listen. Abre el bloque server de tu sitio:
sudo nano /etc/nginx/sites-available/default
Añade el parámetro backlog a las directivas listen:
server {
listen 80 default_server backlog=8192;
listen [::]:80 default_server backlog=8192;
# ... resto de la configuración
}
Importante
backlogsolo puede aparecer una vez por cada combinación de dirección y puerto. Si tienes varios bloquesserveren el puerto 80, ponlo solo en el que llevadefault_server.
El segundo límite habitual son los descriptores de archivo: cada conexión de cliente consume uno, y si Nginx actúa como proxy consume dos. El servicio de systemd arranca con un límite blando de 1024. Sube el límite de los workers y el número de conexiones en /etc/nginx/nginx.conf:
sudo nano /etc/nginx/nginx.conf
Ajusta estas directivas (la primera va en el contexto principal, fuera de cualquier bloque; worker_connections ya existe dentro de events):
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 16384;
}
worker_rlimit_nofile eleva el límite de cada worker sin tocar la unidad de systemd, y worker_connections debe quedar por debajo de él. Valida la configuración y recarga Nginx:
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Comprueba que el socket usa el nuevo backlog:
ss -ltn 'sport = :80'
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 8192 0.0.0.0:80 0.0.0.0:*
Y que los workers tienen el límite de descriptores ampliado:
grep 'Max open files' /proc/$(pgrep -f 'nginx: worker' | head -n 1)/limits
Max open files 65535 65535 files
Paso 5: Probar bajo carga
La herramienta wrk genera muchas conexiones HTTP concurrentes con poco consumo de recursos. Instálala en la máquina cliente:
sudo apt install wrk
Lanza una prueba de 30 segundos con 4 hilos y 2000 conexiones abiertas contra your_server_ip:
wrk -t4 -c2000 -d30s --latency http://your_server_ip/
Running 30s test @ http://your_server_ip/
4 threads and 2000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 38.12ms 21.44ms 412.30ms 81.02%
Req/Sec 13.40k 1.52k 17.90k 72.11%
Latency Distribution
50% 34.80ms
99% 118.66ms
1599208 requests in 30.04s, 1.28GB read
Requests/sec: 53236.18
Si el cliente se queja de Too many open files, sube su propio límite antes de lanzar la prueba con ulimit -n 65535.
Mientras dura la prueba, vuelve a mirar en el servidor los contadores del paso 1:
nstat -az TcpExtListenOverflows TcpExtListenDrops
Si siguen en 0 (o crecen mucho menos que antes de los cambios) y la columna Socket errors de wrk no muestra connect ni timeout, la cola de conexiones ya no es el cuello de botella. A partir de aquí, las mejoras vendrán de la aplicación: caché, compresión, número de workers de PHP-FPM o del backend.
Solución de problemas
sysctl: cannot stat /proc/sys/net/...: No such file or directory: el parámetro no existe en este kernel (típico de tcp_tw_recycle) o tiene una errata. Elimínalo del archivo y vuelve a ejecutar sudo sysctl --system.
tcp_congestion_control vuelve a cubic tras reiniciar: el módulo tcp_bbr no se cargó antes de aplicar sysctl. Comprueba que existe /etc/modules-load.d/bbr.conf y que lsmod | grep tcp_bbr muestra el módulo.
Nginx no arranca tras añadir backlog: nginx -t mostrará duplicate listen options for 0.0.0.0:80. El parámetro está repetido en varios bloques server del mismo puerto; déjalo solo en uno.
Recv-Q crece en el socket en escucha aunque el backlog sea grande: la aplicación no acepta conexiones al ritmo al que llegan. El problema está en los workers (CPU saturada, backend lento), no en el kernel.
Conclusión
Has medido la cola de conexiones del servidor, aplicado un conjunto persistente de ajustes sysctl con BBR, buffers TCP más amplios y un backlog mayor, y has configurado Nginx para aprovecharlo. La clave es repetir la medición del paso 1 tras cada cambio: si un contador no se mueve, el ajuste no hacía falta.
Como siguientes pasos, puedes revisar la gestión de memoria y swap del servidor, habilitar HTTP/2 y la caché de Nginx, o ajustar el programador de E/S si el servidor también sirve mucho contenido desde disco.
