Linux limita los recursos que puede usar cada proceso: cuántos archivos puede tener abiertos, cuántos procesos puede crear un usuario o cuánta memoria puede bloquear. Los valores por defecto son conservadores y un servidor web, una base de datos o un proxy con miles de conexiones simultáneas puede agotarlos, lo que produce errores como Too many open files. En este tutorial aprenderás a consultar estos límites en Ubuntu 24.04, a diagnosticar cuándo se alcanzan y a subirlos en el lugar correcto: limits.conf para las sesiones de usuario y systemd para los servicios.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Todo lo relativo a systemd es igual en Debian 12 y Rocky Linux 9.
  • Un usuario no root con privilegios sudo.
  • Opcional: un servicio como Nginx para seguir el ejemplo del paso 5.

Conceptos básicos: límites blandos y duros

Cada límite tiene dos valores:

  • Blando (soft): el que se aplica realmente al proceso. El propio proceso puede subirlo hasta el límite duro.
  • Duro (hard): el techo. Solo root puede subirlo; un usuario normal únicamente puede bajarlo.

Los límites se heredan de proceso padre a hijo, y quién los fija al principio depende de cómo arranca el proceso:

Cómo arranca el procesoQuién fija sus límites
Sesión SSH o de consolaPAM, con /etc/security/limits.conf y /etc/security/limits.d/
Servicio de systemdLa unidad del servicio (LimitNOFILE=, LimitNPROC=...)
Contenedor DockerEl demonio de Docker o la opción --ulimit

Este es el error más común: limits.conf no afecta a los servicios de systemd. Si subes allí el límite de archivos de Nginx o MySQL y reinicias el servicio, no cambia nada.

Los límites que más se ajustan son estos:

RecursoOpción de ulimitNombre en limits.confDirectiva de systemd
Archivos abiertos-nnofileLimitNOFILE=
Procesos por usuario-unprocLimitNPROC=
Memoria bloqueada-lmemlockLimitMEMLOCK=
Tamaño de volcados de memoria-ccoreLimitCORE=

Paso 1: Consultar los límites actuales

Muestra todos los límites blandos de tu shell:

ulimit -a

Consulta el límite blando y el duro de archivos abiertos:

ulimit -Sn
ulimit -Hn
1024
524288

Los límites de un proceso en ejecución se leen en /proc/PID/limits. Por ejemplo, para el servicio SSH:

cat /proc/$(systemctl show -p MainPID --value ssh)/limits
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max processes             15413                15413                processes
Max open files            1024                 524288               files
Max locked memory         8388608              8388608              bytes
...

Paso 2: Diagnosticar si se está alcanzando un límite

El síntoma típico en los logs de una aplicación es uno de estos mensajes:

socket() failed (24: Too many open files)
accept4() failed (24: Too many open files)
fork: retry: Resource temporarily unavailable

El código 24 es EMFILE: el proceso ha llegado a su límite de archivos abiertos. Cada socket de red cuenta como un archivo, así que un servidor con muchas conexiones lo alcanza antes que ningún otro recurso.

Cuenta los descriptores que tiene abiertos un proceso y compáralos con su límite. Sustituye PID por el identificador del proceso (lo obtienes con pgrep -f nombre o systemctl show -p MainPID --value servicio):

sudo ls /proc/PID/fd | wc -l
grep 'Max open files' /proc/PID/limits

Si el primer número se acerca al límite blando, has encontrado el problema.

Para ver el total del sistema, consulta /proc/sys/fs/file-nr. Sus tres valores son descriptores asignados, libres (siempre 0 en kernels modernos) y el máximo del sistema:

cat /proc/sys/fs/file-nr
2336	0	9223372036854775807

En Ubuntu 24.04 el máximo global es prácticamente ilimitado, así que el cuello de botella siempre es el límite por proceso.

Paso 3: Cambiar el límite en la sesión actual

ulimit modifica los límites del shell actual y de los procesos que lance, y se pierde al cerrar la sesión. Es útil para probar. Sube el límite blando con -S; sin esa opción, bash cambia a la vez el blando y el duro:

ulimit -Sn 65535
ulimit -Sn
65535

Si intentas superar el límite duro sin ser root, obtendrás ulimit: open files: cannot modify limit: Operation not permitted.

Paso 4: Hacer permanente el límite para usuarios con limits.conf

Para las sesiones SSH y de consola, el módulo pam_limits aplica lo que haya en /etc/security/limits.conf y en los archivos de /etc/security/limits.d/. Comprueba que está activo para SSH:

grep pam_limits /etc/pam.d/sshd
session    required     pam_limits.so

En lugar de editar limits.conf, crea un archivo propio:

sudo nano /etc/security/limits.d/90-nofile.conf

Cada línea tiene el formato dominio tipo recurso valor. El dominio puede ser un usuario, un grupo con @ delante o * para todos:

# Usuario de la aplicación
your_user    soft    nofile    65535
your_user    hard    nofile    65535

# Todos los miembros del grupo developers
@developers  soft    nofile    65535
@developers  hard    nofile    65535

# El comodín * no se aplica a root: hay que nombrarlo aparte
root         soft    nofile    65535
root         hard    nofile    65535

Sustituye your_user por tu usuario. Los cambios se aplican en las sesiones nuevas, no hay que reiniciar nada. Abre una nueva conexión SSH y compruébalo:

ulimit -Sn
ulimit -Hn
65535
65535

Paso 5: Subir el límite de un servicio de systemd

Para los servicios, el límite se define en la unidad. No edites el archivo original en /usr/lib/systemd/system/, porque una actualización del paquete lo sobrescribiría. Crea un archivo de ajustes (drop-in) con systemctl edit. Este ejemplo usa Nginx:

sudo systemctl edit nginx

Se abre un editor. Escribe el bloque entre los comentarios que indican dónde añadir contenido:

[Service]
LimitNOFILE=65535

Al guardar, systemd crea /etc/systemd/system/nginx.service.d/override.conf y recarga la configuración. Reinicia el servicio para que el nuevo límite se aplique:

sudo systemctl restart nginx

Verifica el límite que systemd aplica y el que tiene realmente el proceso:

systemctl show nginx -p LimitNOFILE
cat /proc/$(systemctl show -p MainPID --value nginx)/limits | grep 'open files'
LimitNOFILE=65535
Max open files            65535                65535                files

Algunas aplicaciones tienen además su propio ajuste. En Nginx, los procesos worker usan el valor de worker_rlimit_nofile en /etc/nginx/nginx.conf, y worker_connections debe quedar por debajo de él:

worker_rlimit_nofile 65535;

events {
    worker_connections 16384;
}

Comprueba la sintaxis y recarga Nginx:

sudo nginx -t
sudo systemctl reload nginx

Paso 6: Cambiar el valor por defecto de todos los servicios (opcional)

Si varios servicios necesitan el mismo límite, puedes cambiar el valor por defecto del gestor de systemd en lugar de crear un drop-in para cada uno:

sudo mkdir -p /etc/systemd/system.conf.d
sudo nano /etc/systemd/system.conf.d/limits.conf
[Manager]
DefaultLimitNOFILE=65535:524288

El formato blando:duro mantiene el límite duro que Ubuntu ya usa por defecto. Aplica el cambio recargando el gestor y reinicia los servicios afectados:

sudo systemctl daemon-reexec
sudo systemctl restart nginx

Los servicios con su propio LimitNOFILE= siguen usando el suyo.

Paso 7: Límite de procesos y TasksMax

El error fork: retry: Resource temporarily unavailable puede venir de dos límites distintos:

  • nproc: número máximo de procesos e hilos del mismo usuario. Se ajusta igual que nofile, con nproc en limits.d o LimitNPROC= en la unidad. Por defecto ya es alto (unos 15000 en un servidor de 4 GB), así que rara vez es la causa.
  • TasksMax: límite de procesos e hilos de systemd por servicio, mediante cgroups. Es la causa habitual en servicios que crean muchos hilos, como bases de datos o aplicaciones Java.

Consulta el límite de tareas de un servicio y cuántas usa. Sustituye your_service por su nombre:

systemctl show your_service -p TasksMax -p TasksCurrent
TasksMax=4618
TasksCurrent=187

Si TasksCurrent se acerca a TasksMax, súbelo con sudo systemctl edit your_service:

[Service]
TasksMax=16384

Después reinicia el servicio con sudo systemctl restart your_service.

Solución de problemas

El límite no cambia al conectar por SSH. Comprueba que no hay errores de sintaxis en /etc/security/limits.d/; pam_limits ignora las líneas que no entiende y lo registra en sudo journalctl -t sshd. Recuerda que el blando nunca puede superar al duro.

El límite no se aplica con sudo -u o su. Los límites dependen de la configuración PAM de cada herramienta. Para procesos permanentes, usa un servicio de systemd con LimitNOFILE= en lugar de lanzarlos desde una sesión.

El servicio sigue con 1024 después del drop-in. Revisa que el archivo tiene la cabecera [Service], ejecuta systemctl cat nombre_servicio para ver la configuración combinada y reinicia el servicio, no basta con reload.

Un contenedor Docker muestra otro límite. Los contenedores heredan los límites del demonio de Docker. Fíjalos por contenedor con docker run --ulimit nofile=65535:65535 o con la clave ulimits en Docker Compose.

Conclusión

Ya sabes leer los límites de cualquier proceso, detectar cuándo se agotan y subirlos donde corresponde: /etc/security/limits.d/ para las sesiones de usuario y LimitNOFILE o TasksMax en systemd para los servicios. Como siguientes pasos, revisa los ajustes propios de tus aplicaciones (como worker_connections en Nginx o max_connections en MySQL), ajusta los parámetros del kernel con sysctl y monitoriza el número de descriptores abiertos de tus servicios críticos.