Erreur « Too many open files » : relever la limite d’un service systemd

Un service tombe sur la ligne Too many open files alors que la même action relancée à la main dans un terminal fonctionne. Le message est errno 24, soit EMFILE : le processus a atteint son propre plafond de descripteurs de fichiers, pas la mémoire et pas le disque. Un processus lancé par une unité systemd démarre avec 1024 descripteurs en limite souple, valeur fixée par DefaultLimitNOFILE=1024:524288 dans systemd, et /etc/security/limits.conf n’y change rien.
Sommaire
- EMFILE (24, « Too many open files ») n’est pas ENFILE
- Un service systemd démarre à 1024 descripteurs, pas la session SSH de l’administrateur
- Relever la limite en quatre étapes
- daemon-reload ne change pas un processus en cours, et ulimit dans le shell ne le touche pas
- nginx produit deux messages distincts selon l’endroit où la limite tombe
- fs.nr_open plafonne toute valeur, y compris pour root
- Relever la limite ou corriger la fuite
- Ce qu’on nous demande le plus
EMFILE (24, « Too many open files ») n’est pas ENFILE
La page open(2) des pages de manuel Linux tranche en deux erreurs voisines. EMFILE signifie « the per-process limit on the number of open file descriptors has been reached ». ENFILE signifie « the system-wide limit on the total number of open files has been reached ». Le premier se règle sur le processus fautif, le second se règle au niveau du noyau pour toute la machine. Confondre les deux conduit à modifier une valeur qui n’est pas la bonne.
Chaque socket, chaque pipe, chaque fichier de log ouvert et chaque connexion réseau consomme un descripteur. Sur un serveur web ou un proxy, ce sont les sockets qui saturent les premiers. La valeur errno 24 renvoyée par strerror(24) est exactement la chaîne Too many open files affichée par le journal.
Un service systemd démarre à 1024 descripteurs, pas la session SSH de l’administrateur
systemd-system.conf(5) documente les valeurs par défaut appliquées à tout processus issu d’une unité. DefaultLimitNOFILE= prend la valeur 1024:524288, soit 1024 en limite souple et 524288 en limite dure. Le même document précise que PID 1 relève sa propre limite mais que celle-ci est ramenée aux valeurs par défaut pour tous les processus enfants qu’il lance. Un service démarre donc à 1024 descripteurs, même si l’administrateur a relevé la sienne dans son terminal.
Le contenu de /etc/security/limits.conf ne s’applique pas à ce cas de figure. limits.conf(5) décrit le module pam_limits, qui applique ces limites aux sessions de connexion. pam_limits(8) demande en plus d’inscrire la ligne session required pam_limits.so dans le fichier /etc/pam.d/ du service concerné. Or systemd.exec(5) écrit à propos de PAMName= : « If not set, no PAM session will be opened for the executed processes ». Une unité sans PAMName= n’ouvre aucune session PAM, donc aucun module PAM n’est appelé et limits.conf n’est jamais lu.
| Couche | Fichier | Ce qu’elle limite | Valeur par défaut |
|---|---|---|---|
| systemd | unité ou drop-in | le service et ses processus enfants | 1024 souple, 524288 dur |
| pam_limits | /etc/security/limits.d/*.conf | les sessions de connexion seulement | selon la configuration locale |
| noyau | /proc/sys/fs/nr_open | plafond de RLIMIT_NOFILE par processus | 1048576 |
| noyau | /proc/sys/fs/file-max | total des fichiers ouverts sur la machine | calculé au démarrage |
Relever la limite en quatre étapes
L’exemple porte sur nginx.service, transposable à n’importe quelle unité. Les valeurs par défaut citées sont celles de systemd 252.39 sous Debian 12, de systemd 257 sous Debian 13 et de systemd 255.4 sous Ubuntu 24.04 LTS.
-
Lire la limite réellement en vigueur dans le processus qui tourne.
systemctl show -p MainPID --value nginx.service grep 'Max open files' /proc/1234/limits
Le numéro de PID remplace
1234. La ligne attendue est celle-ci :Max open files 1024 524288 files
La première colonne chiffrée est la limite souple appliquée en ce moment, la seconde la limite dure. Un
1024en tête confirme que le service tourne encore sur la valeur par défaut de systemd. -
Compter les descripteurs ouverts et repérer ceux qui s’accumulent.
ls /proc/1234/fd | wc -l lsof -p 1234 | awk '{print $5}' | sort | uniq -c | sort -rn | headLe premier comptage doit approcher 1024 si la limite est bien la cause de l’arrêt. Le second trie les descripteurs par type : une majorité de
sockdésigne des connexions réseau. Une série de sockets bloquées enCLOSE_WAIT, visible avecss -tan state close-wait, et un compteur qui monte sans jamais redescendre désignent une fuite. Relever la limite ne fait alors que reculer la panne. La ligne exacte qui précède l’arrêt se lit dans le journal du service, dont les commandes d’extraction sont réunies dans services et journaux systemd. -
Écrire la limite dans un drop-in et recharger systemd.
systemctl edit nginx.service systemctl daemon-reload systemctl restart nginx.service
L’éditeur ouvert par
systemctl editannonce le chemin du fichier de substitution. La seule section à renseigner contient deux lignes :[Service] LimitNOFILE=65535
Une valeur unique fixe à la fois la limite souple et la limite dure.
systemd.exec(5)le formule ainsi : une limite s’écrit « either as single value to set a specific soft and hard limit to the same value, or as colon-separated pair soft:hard ». Aucun suffixe n’est admis ici : les suffixes K, M, G concernent les limites exprimées en octets. La commande suivante doit afficher le drop-in avec son chemin complet :systemctl cat nginx.service
# /etc/systemd/system/nginx.service.d/override.conf [Service] LimitNOFILE=65535
-
Vérifier que le nouveau processus a hérité de la valeur.
systemctl show -p MainPID --value nginx.service grep 'Max open files' /proc/1234/limits
Le PID a changé après le redémarrage, et la ligne doit maintenant afficher les deux colonnes à 65535 :
Max open files 65535 65535 files
Si la ligne conserve 1024, le service n’a pas été redémarré ou le drop-in n’est pas en place dans
/etc/systemd/system/nginx.service.d/override.conf.
daemon-reload ne change pas un processus en cours, et ulimit dans le shell ne le touche pas
systemctl daemon-reload relit les fichiers d’unité sans relancer les services. Tant que systemctl restart n’a pas été exécuté, /proc/1234/limits garde 1024 et le message revient dès la connexion suivante. Le second piège est la limite dure : la commande ulimit -n 65535 échoue sur un message littéral de bash, bash: ulimit: open files: cannot modify limit: Operation not permitted, tant que la limite dure n’a pas été relevée au préalable. Le changement obtenu avec ulimit ne vaut que pour le shell courant et ses enfants, jamais pour un démon déjà lancé.
Pour un service qu’on ne peut pas couper, prlimit(1) agit sur un processus vivant :
prlimit --pid 1234 --nofile=65535:65535
La modification tient jusqu’au prochain redémarrage du service. Elle est possible parce que la limite dure héritée vaut 524288 par défaut ; toute valeur supérieure à /proc/sys/fs/nr_open est refusée.
nginx produit deux messages distincts selon l’endroit où la limite tombe
[crit] 1234#0: accept4() failed (24: Too many open files) [warn] 1234#0: 2048 worker_connections are more than open file resource limit: 1024
Le premier vient de accept4() quand nginx ne peut plus accepter de connexion cliente. Le code source de nginx traite EMFILE et ENFILE en niveau critique et le journal accompagne le nom de l’appel système du numéro d’erreur et de son texte. Le second avertit que la directive worker_connections dépasse la limite de descripteurs héritée. La documentation de nginx rappelle que worker_connections vaut 512 par défaut et que « the actual number of simultaneous connections cannot exceed the current limit on the maximum number of open files, which can be changed by worker_rlimit_nofile ». Deux réglages, dans deux fichiers différents, sont donc en jeu.
| Réglage | Fichier | Portée | Valeur par défaut |
|---|---|---|---|
| worker_connections 4096; | /etc/nginx/nginx.conf, bloc events | connexions simultanées par worker | 512 |
| worker_rlimit_nofile 65535; | /etc/nginx/nginx.conf, niveau principal | descripteurs de chaque worker | non défini |
| LimitNOFILE=65535 | drop-in de l’unité nginx.service | service et processus enfants | 1024 souple, 524288 dur |
worker_rlimit_nofile ne peut pas dépasser la limite dure héritée de systemd. Relever d’abord LimitNOFILE dans l’unité, puis fixer worker_rlimit_nofile à la même valeur évite l’inversion des deux opérations.
fs.nr_open plafonne toute valeur, y compris pour root
proc_sys_fs(5) décrit /proc/sys/fs/nr_open comme le plafond de RLIMIT_NOFILE : « This file imposes a ceiling on the value to which the RLIMIT_NOFILE resource limit can be raised. This ceiling is enforced for both unprivileged and privileged process. The default value in this file is 1048576. » Une unité réglée sur LimitNOFILE=2000000 demande donc une valeur inapplicable sur un noyau laissé par défaut. La limite globale fs.file-max, elle, reste distincte et relève des erreurs ENFILE. Le comportement exact de systemd face à une valeur supérieure à fs.nr_open n’a pas pu être vérifié sur une machine Debian dans le cadre de cette rédaction.
Relever la limite ou corriger la fuite
La limite par défaut n’est pas un défaut de configuration à elle seule. Un service qui garde un nombre stable de descripteurs autour de 900 sur 1024 a besoin d’une valeur plus haute, et 65535 est alors une marge confortable. Un service dont le compteur progresse de quelques dizaines par heure jusqu’au blocage ne doit pas seulement voir sa limite relevée : la fuite de sockets ou de fichiers reste à corriger, sans quoi le même incident reviendra avec un plafond plus élevé et un délai plus long. Comme les autres paramètres d’un serveur Linux, cette limite se règle au niveau de l’unité et non au niveau de la session qui l’a lancée.
Ce qu’on nous demande le plus
Pourquoi /etc/security/limits.conf n’a-t-il aucun effet sur mon service ?
Parce que ce fichier est lu par le module PAM pam_limits, qui n’intervient que dans les sessions de connexion. Une unité systemd qui ne définit pas PAMName= n’ouvre aucune session PAM : systemd.exec(5) indique que « no PAM session will be opened for the executed processes ». La limite est alors fixée par DefaultLimitNOFILE, à 1024 en souple.
Quelle est la limite par défaut d’un service systemd ?
1024 descripteurs en limite souple et 524288 en limite dure, valeur documentée sous le nom DefaultLimitNOFILE=1024:524288 dans systemd-system.conf(5). Elle s’applique à systemd 252.39 sous Debian 12, à systemd 257 sous Debian 13 et à systemd 255.4 sous Ubuntu 24.04 LTS tant qu’aucun fichier system.conf ne la surcharge.
Faut-il redémarrer le service après systemctl daemon-reload ?
Oui. daemon-reload relit les fichiers d’unité mais laisse tourner les processus existants sur l’ancienne limite. Sans systemctl restart, /proc/PID/limits continue d’afficher 1024 et l’erreur revient.
Peut-on relever la limite d’un processus sans le redémarrer ?
Oui, avec prlimit : prlimit –pid 1234 –nofile=65535:65535 modifie les limites du processus vivant. La valeur est perdue au prochain redémarrage du service, et la limite dure du service doit rester égale ou supérieure à la valeur demandée.
Une valeur très haute comme 999999 est-elle acceptée ?
Pas au-delà du plafond du noyau : /proc/sys/fs/nr_open limite RLIMIT_NOFILE et vaut 1048576 par défaut. Une directive LimitNOFILE supérieure à cette valeur ne peut pas être appliquée.
Sources : systemd-system.conf(5), DefaultLimitNOFILE, systemd.exec(5), LimitNOFILE et PAMName, limits.conf(5), pam_limits(8), open(2), EMFILE et ENFILE, proc_sys_fs(5), fs.nr_open, prlimit(1), nginx, worker_connections et worker_rlimit_nofile, nginx, ngx_event_accept.c.