« Too many open files » est un de ces messages qui semble être un réglage simple mais qui se transforme en après-midi de fausses pistes. Vous lancez ulimit -n 1048576, il affiche le grand nombre demandé, vous redémarrez le service, et il s’écroule encore sous charge. Votre terminal dit « corrigé. » La production dit « bel essai. »
Sur Debian 13, la bonne correction n’est la plupart du temps pas plus de magie shell. C’est systemd. Plus précisément : la bonne couche systemd, la bonne unité, et une vérification qui ne vous ment pas. Faisons-le correctement, comme vous voudriez le faire à 02:00 avec des clients qui regardent.
Ce que signifie vraiment « Trop de fichiers ouverts » (et ce que cela n’est pas)
Sur Linux, « Too many open files » correspond typiquement à l’une des deux erreurs :
- EMFILE : le processus a atteint sa limite de descripteurs de fichiers par processus (RLIMIT_NOFILE). C’est celui que vous corrigez avec systemd
LimitNOFILE=(ou équivalent). C’est personnel. Un seul processus a dépassé son budget. - ENFILE : le système a atteint une limite globale de table de fichiers. C’est plus rare sur les noyaux modernes, mais ça arrive dans des cas pathologiques. C’est commun. Tout l’hôte a dépassé son budget.
Aussi : « fichiers ouverts » est une expression imprécise. Il s’agit de descripteurs de fichiers. Ces descripteurs peuvent représenter des fichiers réguliers, des répertoires, des sockets, des pipes, des eventfd, des signalfd, des watches inotify, des instances epoll, et une poignée d’autres objets qui font sens après une longue semaine.
Deux conséquences :
- Vous pouvez épuiser les FDs sans qu’un seul fichier de log soit « ouvert ». Les services à haute connexion meurent ainsi tout le temps.
- Augmenter les limites sans réfléchir peut masquer des fuites, dissimuler une mauvaise gestion des connexions, et déplacer la défaillance vers un autre sous-système (mémoire, tenue de comptes du noyau, ou votre backend de stockage).
Une citation qui tient encore en exploitation, reformulée parce que je ne parie pas sur une formulation parfaite : idée paraphrasée
— « L’espoir n’est pas une stratégie. » (souvent attribuée dans les cercles d’ingénierie à des praticiens comme Gene Kranz, et répété ad nauseam en fiabilité). Dans ce contexte : ne comptez pas sur le fait que votre ulimit ait fait quelque chose. Prouvez-le, puis corrigez au niveau qui démarre réellement le service.
Blague #1 : « Too many open files » est la façon dont Linux dit que votre processus a des problèmes d’engagement. Il n’arrête pas d’ouvrir des choses et refuse de les fermer.
Feuille de diagnostic rapide
Ceci est la section « arrêtez de scroller et faites ces trois vérifications ». Elle est ordonnée comme je l’exécute quand le pager est chaud.
Première étape : identifier s’il s’agit d’un problème par processus (EMFILE) ou global (ENFILE)
- Cherchez l’erreur exacte dans les logs (logs applicatifs, journal). EMFILE/ENFILE est décisif.
- Si vous ne récupérez pas l’errno exact, vérifiez rapidement l’usage des FDs par processus et le RLIMIT (voir les tâches ci-dessous).
Deuxième étape : vérifier le RLIMIT_NOFILE réel du service en cours (pas celui de votre shell)
- Récupérez le PID du processus principal du service.
- Lisez
/proc/<pid>/limits. Ce fichier est la réalité.
Troisième étape : trouver ce qui a fixé la limite (unité systemd, drop-in, valeurs par défaut, PAM)
- Si le processus est démarré par systemd, l’unité (et les valeurs par défaut de systemd) l’emportent.
- Si c’est un service de session utilisateur, le gestionnaire utilisateur a ses propres valeurs par défaut.
- Les limites PAM affectent souvent les connexions interactives et certains services, mais pas les services système typiques.
Si vous ne faites qu’une chose aujourd’hui : arrêtez de faire confiance à la sortie de ulimit dans votre shell de connexion comme preuve que votre démon a cette limite. C’est comme cela que vous finissez par « corriger » les choses trois fois.
La pile des limites sur Debian 13 : qui l’emporte et pourquoi
Linux dispose de limites de ressources (rlimits). systemd peut les définir pour les services. PAM peut les définir pour les sessions de connexion. Les shells peuvent les définir pour les processus enfants. Les conteneurs peuvent ajouter leurs propres contraintes. Et le noyau a des maxima système.
Voici la hiérarchie pratique sur laquelle la plupart des gens trébuchent :
- Maxima du noyau et tables système (par ex.
fs.file-max). Ce sont des frontières strictes et des ressources partagées. - Rlimits par processus (RLIMIT_NOFILE). Chaque processus reçoit une limite « soft » et une limite « hard ». La limite soft est celle que vous atteignez. La limite hard est le plafond auquel vous pouvez élever la soft sans privilège.
- Paramètres du gestionnaire de services systemd qui fixent les rlimits au moment de l’exec, et qui s’appliquent à l’arbre de processus du service.
- Sessions utilisateur (PAM + gestionnaire utilisateur) qui peuvent définir des rlimits pour les shells interactifs et les services utilisateur.
- Shell
ulimitn’est que la vue du shell et ce qu’il transmet à ses enfants. Cela ne change rien pour les démons déjà en cours d’exécution.
La réalité clé de Debian 13 : la plupart des démons serveur sont démarrés par systemd. Cela signifie que le fichier d’unité et les valeurs par défaut de systemd importent plus que tout ce que vous tapez dans un shell.
De plus, systemd n’est pas seulement un init glorifié. C’est un superviseur de processus qui peut définir des limites, isoler des ressources, et contrôler les descripteurs de fichiers d’une manière qui contourne vos hypothèses. Si votre modèle mental est « /etc/security/limits.conf contrôle tout », vous perdrez du temps.
La bonne correction systemd : LimitNOFILE, valeurs par défaut et drop-ins
Il y a trois façons raisonnables d’augmenter les limites de FD pour un service systemd sur Debian 13. Choisissez selon le rayon d’impact.
Option A (recommandée) : définir LimitNOFILE dans un drop-in pour le service spécifique
Ceci est la correction chirurgicale. Elle modifie une seule unité. Elle survit aux mises à jour du paquet. Elle est auditable.
Créez un drop-in :
cr0x@server:~$ sudo systemctl edit nginx.service
Puis ajoutez :
cr0x@server:~$ sudo tee /etc/systemd/system/nginx.service.d/limits.conf >/dev/null <<'EOF'
[Service]
LimitNOFILE=262144
EOF
Rechargez et redémarrez :
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart nginx.service
cr0x@server:~$ systemctl status nginx.service --no-pager
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Drop-In: /etc/systemd/system/nginx.service.d
└─limits.conf
Active: active (running)
Si vous ne voyez pas le Drop-In listé, votre drop-in est mal placé, ou vous avez oublié le daemon-reload.
Option B : changer les valeurs par défaut système pour tous les services système (gros marteau)
Vous pouvez définir des limites par défaut dans /etc/systemd/system.conf et/ou /etc/systemd/user.conf :
cr0x@server:~$ sudo sed -n '1,120p' /etc/systemd/system.conf
# This file is part of systemd.
#DefaultLimitNOFILE=1024:524288
Décommentez et réglez, par exemple :
cr0x@server:~$ sudo perl -0777 -pe 's/^#?DefaultLimitNOFILE=.*/DefaultLimitNOFILE=65536:1048576/m' -i /etc/systemd/system.conf
Puis :
cr0x@server:~$ sudo systemctl daemon-reexec
Avertissement d’opinion : L’option B est la manière dont vous « corrigez » accidentellement un service en donnant à tous les services la permission d’endommager l’hôte de nouvelles façons. Ne l’utilisez que si vous avez une politique de flotte et une raison.
Option C : configurer les propres limites de l’application (uniquement si applicable)
CERTAINS daemons ont leurs propres réglages de limite (par exemple via worker_rlimit_nofile dans nginx) qui doivent être alignés avec RLIMIT_NOFILE. Ceux-ci ne remplacent pas les limites de systemd ; ils se superposent. Si systemd plafonne à 8192, la configuration de votre appli peut demander 100k toute la journée et obtient quand même 8192.
Donc l’ordre correct est : définir LimitNOFILE dans systemd d’abord, puis ajuster les limites au niveau de l’application pour les utiliser.
Qu’en est-il de /etc/security/limits.conf ?
Ce fichier n’est pas inutile, il est juste fréquemment hors sujet pour les services système. /etc/security/limits.conf est appliqué par PAM dans des contextes de connexion. Si vous démarrez un démon via systemd au boot, PAM ne fait pas partie de l’histoire. Debian 13 n’est pas spécial ici ; c’est juste que Linux moderne est sans vergogne multicouche.
Blague #2 : ulimit est comme crier votre salaire désiré dans le couloir. Ça fait du bien, mais la paie fait quand même ce qu’elle est configurée pour faire.
Vérification qui ne ment pas : /proc, systemctl et processus en cours
Si vous adoptez une habitude opérationnelle de cet article, que ce soit celle-ci : vérifiez les limites dans le processus en cours, pas dans le shell qui se trouve sur votre écran.
Trois angles fiables :
- /proc/<pid>/limits montre les limites soft/hard actuelles pour ce processus.
- systemctl show peut afficher les propriétés de l’unité et les réglages effectifs (mais vérifiez quand même dans /proc).
- Compter les FDs ouverts via
/proc/<pid>/fdvous dit si vous approchez du plafond, fuyez des descripteurs, ou subissez des pics pendant la charge.
Et rappelez-vous : les services ont souvent plusieurs processus (master + workers). Celui qui échoue n’est peut‑être pas celui que vous avez regardé. Vérifiez le processus qui émet EMFILE.
Tâches pratiques (commandes + interprétation + décisions)
Ce sont des tâches de terrain. Exécutez-les sur Debian 13. Chacune inclut : commande, sortie d’exemple, interprétation, et une décision.
Tâche 1 : Confirmer le code d’erreur dans le journal
cr0x@server:~$ sudo journalctl -u nginx.service -n 50 --no-pager
Dec 28 10:12:01 server nginx[1423]: accept4() failed (24: Too many open files)
Dec 28 10:12:01 server nginx[1423]: worker_connections are not enough
Signification : errno 24 est EMFILE. C’est RLIMIT_NOFILE par processus, pas les tables noyau globales.
Décision : Concentrez-vous sur LimitNOFILE pour les processus nginx et sur les réglages propres de nginx pour les workers. Ne touchez pas à fs.file-max pour l’instant.
Tâche 2 : Trouver le MainPID du service
cr0x@server:~$ systemctl show -p MainPID --value nginx.service
1423
Signification : PID 1423 est le processus principal suivi par systemd.
Décision : Utilisez ce PID pour la vérification initiale, mais vérifiez aussi les PIDs des workers.
Tâche 3 : Lire le RLIMIT_NOFILE réel depuis /proc
cr0x@server:~$ sudo awk '/Max open files/ {print}' /proc/1423/limits
Max open files 8192 8192 files
Signification : Soft=8192, Hard=8192. Vous atteindrez EMFILE autour de ~8192 descripteurs dans ce processus.
Décision : Élevez-le via systemd (drop-in). Un ulimit dans un shell ne changera pas ce processus en cours d’exécution.
Tâche 4 : Confirmer ce que systemd pense être la limite de l’unité
cr0x@server:~$ systemctl show nginx.service -p LimitNOFILE
LimitNOFILE=8192
Signification : systemd applique actuellement 8192 au service.
Décision : Ajoutez un drop-in avec un LimitNOFILE plus élevé, puis redémarrez le service.
Tâche 5 : Créer et vérifier un override drop-in
cr0x@server:~$ sudo systemctl edit nginx.service
cr0x@server:~$ sudo systemctl cat nginx.service
# /lib/systemd/system/nginx.service
[Unit]
Description=A high performance web server and a reverse proxy server
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=262144
Signification : Le drop-in est chargé et remplace l’unité.
Décision : Rechargez et redémarrez. Si le drop-in n’apparaît pas dans systemctl cat, il n’est pas en vigueur.
Tâche 6 : Redémarrer et revérifier les limites dans /proc
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart nginx.service
cr0x@server:~$ systemctl show -p MainPID --value nginx.service
1777
cr0x@server:~$ sudo awk '/Max open files/ {print}' /proc/1777/limits
Max open files 262144 262144 files
Signification : Le processus en cours a maintenant la limite augmentée. C’est la condition de victoire.
Décision : Passez à l’analyse de l’usage/la pression : fuyez-vous encore des FDs, ou le plafond était-il simplement trop bas ?
Tâche 7 : Compter les FDs ouverts actuels pour un processus
cr0x@server:~$ sudo ls /proc/1777/fd | wc -l
412
Signification : 412 descripteurs ouverts à cet instant. C’est une photo instantanée.
Décision : Si vous voyez ceci monter régulièrement sans redescendre, suspectez une fuite. Si ça pique lors du trafic, suspectez la concurrence de pointe et ajustez en conséquence.
Tâche 8 : Identifier quels descripteurs dominent
cr0x@server:~$ sudo ls -l /proc/1777/fd | head -n 12
lrwx------ 1 www-data www-data 64 Dec 28 10:20 0 -> /dev/null
lrwx------ 1 www-data www-data 64 Dec 28 10:20 1 -> /var/log/nginx/access.log
lrwx------ 1 www-data www-data 64 Dec 28 10:20 2 -> /var/log/nginx/error.log
lrwx------ 1 www-data www-data 64 Dec 28 10:20 3 -> socket:[23451]
lrwx------ 1 www-data www-data 64 Dec 28 10:20 4 -> socket:[23452]
Signification : Beaucoup de sockets implique une charge de connexion. Beaucoup de fichiers réguliers peut impliquer la gestion des logs/fichiers, du cache, ou des fuites dans l’E/S fichier.
Décision : Si les sockets dominent, ajustez la gestion des connexions (backlog, keepalive, nombre de workers). Si ce sont des fichiers, cherchez des fuites de fichiers ou le comportement de rotation.
Tâche 9 : Vérifier les limites par utilisateur pour les sessions interactives (PAM)
cr0x@server:~$ ulimit -Sn
1024
cr0x@server:~$ ulimit -Hn
1048576
Signification : Votre shell de connexion a soft 1024, hard 1048576. Cela ne s’applique pas automatiquement aux services systemd.
Décision : Si des développeurs exécutent des charges manuellement (pas via systemd), ajustez les limites PAM. Sinon, concentrez-vous sur les limites au niveau des unités.
Tâche 10 : Vérifier la pression système globale des descripteurs de fichiers
cr0x@server:~$ cat /proc/sys/fs/file-nr
2976 0 9223372036854775807
Signification : Le premier nombre est les handles de fichier alloués ; le troisième est le max. Sur beaucoup de systèmes le max est effectivement énorme (ou « presque infini »).
Décision : Si l’alloué est proche du max (sur des systèmes avec un vrai plafond), vous avez un problème au niveau hôte. Sinon, il s’agit probablement d’un problème par processus.
Tâche 11 : Inspecter le maximum du noyau de fichiers ouverts par processus (fs.nr_open)
cr0x@server:~$ cat /proc/sys/fs/nr_open
1048576
Signification : C’est le plafond du noyau pour RLIMIT_NOFILE. Vous ne pouvez pas fixer des limites par processus au‑dessus de ceci.
Décision : Si vous avez vraiment besoin de > 1,048,576 FDs par processus (rare, mais pas impossible pour des boxes proxy), vous devez augmenter fs.nr_open via sysctl et assurer de la marge mémoire.
Tâche 12 : Vérifier les limites effectives de l’unité après redémarrage
cr0x@server:~$ systemctl show nginx.service -p LimitNOFILE
LimitNOFILE=262144
Signification : systemd pense qu’il applique la nouvelle limite.
Décision : Si systemctl show affiche la nouvelle valeur mais que /proc/<pid>/limits ne l’a pas, vous avez redémarré la mauvaise chose, vous regardez le mauvais PID, ou le service utilise un wrapper qui exec ailleurs. Suivez la chaîne de PID.
Tâche 13 : Trouver tous les PIDs dans le cgroup du service et vérifier un worker
cr0x@server:~$ systemctl show -p ControlGroup --value nginx.service
/system.slice/nginx.service
cr0x@server:~$ cat /sys/fs/cgroup/system.slice/nginx.service/cgroup.procs | head
1777
1780
1781
1782
cr0x@server:~$ sudo awk '/Max open files/ {print}' /proc/1780/limits
Max open files 262144 262144 files
Signification : Les workers ont hérité de la même limite. Bon signe.
Décision : Si les workers diffèrent, vous pouvez avoir des chemins de lancement mixtes ou des wrappers d’exec. Corrigez le chemin de démarrage du service.
Tâche 14 : Capturer la croissance des FDs dans le temps (vérification pauvre d’une fuite)
cr0x@server:~$ for i in 1 2 3 4 5; do date; sudo ls /proc/1777/fd | wc -l; sleep 5; done
Sun Dec 28 10:24:01 UTC 2025
412
Sun Dec 28 10:24:06 UTC 2025
418
Sun Dec 28 10:24:11 UTC 2025
430
Sun Dec 28 10:24:16 UTC 2025
446
Sun Dec 28 10:24:21 UTC 2025
462
Signification : Le compte est en hausse. Cela peut être une augmentation légitime de la charge ou une fuite.
Décision : Si le trafic est stable mais que les FDs augmentent de façon monotone, investiguez les fuites (sockets en amont bloqués, fichiers non fermés, bibliothèques boguées). Augmenter les limites ne fait que gagner du temps.
Tâche 15 : Confirmer que le service n’est pas contraint par un wrapper comme su/sudo ou une tâche cron
cr0x@server:~$ ps -o pid,ppid,cmd -p 1777
PID PPID CMD
1777 1 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
Signification : PPID 1 indique systemd (ou init) comme parent. C’est un service système, donc les limites systemd s’appliquent.
Décision : Si le PPID est un shell, cron, ou un wrapper, corrigez la méthode de lancement ou assurez-vous que le wrapper définit correctement les rlimits (généralement : arrêtez de faire ça, utilisez une unité).
Trois mini-histoires d’entreprise issues du terrain
Mini-histoire 1 : l’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne exploitait une couche d’API basée sur Debian derrière un reverse proxy. Lors d’un événement marketing, les requêtes ont commencé à échouer avec des 502 sporadiques. L’ingénieur d’astreinte a fait la routine : SSH, ulimit -n, a vu un grand nombre (ils l’avaient réglé des mois auparavant), et a conclu « pas une limite de FD ». Ils ont alors cherché le CPU et les graphs réseau.
L’incident s’est prolongé parce que le symptôme était incohérent. Seuls certains nœuds échouaient. Seulement pendant le pic. Et les logs étaient bruyants : resets de connexion, timeouts upstream, la soupe habituelle du chaos. La ligne clé — accept4() failed (24: Too many open files) — était présente, mais noyée parmi d’autres messages. Quand ils s’y sont finalement intéressés, ils ont regardé /proc/<pid>/limits pour les workers du proxy et trouvé un RLIMIT minuscule comparé au shell.
L’hypothèse erronée était simple : « Si mon shell a 200k, le service aussi. » Cette hypothèse était vraie il y a des années dans certaines configurations où les démons étaient lancés par des scripts dans des environnements de type login. Sous systemd, c’est majoritairement faux.
La correction fut un drop-in d’unité : LimitNOFILE=131072, suivi d’un redémarrage en vagues. Le postmortem n’a pas porté sur le nombre de FD ; il a porté sur la discipline de vérification. Leur nouvelle règle fut brutalement pratique : aucun incident n’est clos tant que quelqu’un n’a pas collé /proc/<pid>/limits dans le ticket.
Mini-histoire 2 : l’optimisation qui a mal tourné
Une équipe de services financiers exploitait une passerelle de messages avec réutilisation de connexions agressive. Quelqu’un a remarqué que l’augmentation de la limite de FD « réglait » les EMFILE occasionnels et a décidé d’aller plus loin. Ils ont poussé LimitNOFILE de quelques dizaines de milliers à presque le max du noyau, l’ont déployé et ont déclaré victoire.
En quelques jours, l’hôte a commencé à se comporter bizarrement lors des rafales. Plus d’EMFILE — maintenant c’étaient des pressions mémoire et des pics de latence sporadiques. Il s’est avéré qu’avec la nouvelle marge, la passerelle gardait volontiers beaucoup plus de sockets vivantes pendant la lenteur upstream. Cela a augmenté l’utilisation mémoire du noyau pour les buffers de sockets et la tenue de comptes. Leur goulot réel n’était pas « pas assez de FDs », mais « nous ne faisons pas décrocher la charge quand l’amont est lent ».
L’optimisation a mal tourné parce qu’elle a supprimé un fusible de sécurité. Le cap FD forçait une sorte de backpressure accidentel. L’enlever sans ajouter de backpressure explicite a déplacé le mode de défaillance vers quelque chose de plus laid : latence de queue et retries en cascade.
Ils ont fini par se mettre d’accord sur une limite FD modérée, plus des bornes strictes de pool de connexions et des timeouts. La leçon n’était pas « ne pas augmenter les limites ». C’était « augmentez les limites seulement après avoir rendu explicite votre comportement de concurrence ». Les limites sont des garde‑rails. Si vous les enlevez, installez d’abord de meilleurs garde‑rails.
Mini-histoire 3 : la pratique ennuyeuse mais correcte qui a sauvé la journée
Une grande équipe de plateforme interne exploitait des systèmes Debian avec un mélange de services système et de services utilisateur. Ils avaient une habitude que certains appelaient « paranoïaque » : toute modification liée à la performance exigeait un extrait de vérification dans la demande de changement. Pour l’ajustement des FD, cet extrait comprenait toujours (1) les propriétés de l’unité systemd, (2) les /proc limits pour le Main PID et un worker, et (3) un comptage FD avant/après pendant un test de charge.
Un après-midi, un déploiement routinier a introduit une fuite FD subtile dans une bibliothèque cliente gRPC. Ce n’était pas catastrophique immédiatement ; cela a pris des heures. Leur monitoring a détecté la tendance : les FDs ouverts augmentaient linéairement. Parce que leur checklist de déploiement incluait « vérification de la tendance FD », ils l’ont remarqué tôt sur le canari.
Ils ont reverti avant que la fuite atteigne le hard cap. Pas d’incident. Pas de bridge d’astreinte. Juste un ingénieur légèrement agacé et un graphe propre.
Rien d’héroïque n’est arrivé. C’est le point. Les pratiques ennuyeuses sauvent plus de systèmes que les idées brillantes. La vérification bat les impressions.
Erreurs courantes : symptôme → cause racine → correction
1) « J’ai augmenté ulimit, mais le service affiche toujours Too many open files »
Symptôme : ulimit -n dans votre shell semble élevé ; les logs du démon montrent EMFILE.
Cause racine : Le démon est démarré par systemd avec un LimitNOFILE plus bas (ou la valeur par défaut).
Correction : Ajoutez un drop-in systemd avec LimitNOFILE=, rechargez, redémarrez, vérifiez via /proc/<pid>/limits.
2) « systemctl show indique LimitNOFILE élevé, mais /proc montre toujours bas »
Symptôme : systemctl show -p LimitNOFILE affiche votre nouvelle valeur ; le PID en cours est inchangé et toujours plafonné.
Cause racine : Vous n’avez pas redémarré le service, ou vous vérifiez le mauvais processus (worker vs master), ou le service fork/exec dans un PID différent que vous n’avez pas inspecté.
Correction : Redémarrez l’unité, obtenez le nouveau MainPID, inspectez les PIDs du cgroup, vérifiez /proc/<pid>/limits pour le processus en échec.
3) « Après avoir augmenté les limites, l’hôte devient instable sous charge »
Symptôme : Plus d’EMFILE, mais latence, pression mémoire, ou backlog réseau qui augmente.
Cause racine : Des limites FD plus élevées ont permis une concurrence plus grande sans backpressure. Buffers de socket et usage mémoire noyau augmentent.
Correction : Ajoutez des contrôles de concurrence explicites (pools de connexion, plafonds de workers, timeouts). Fixez des limites FD alignées sur le design, pas l’ego.
4) « Seuls les processus lancés par les utilisateurs échouent ; les services système vont bien »
Symptôme : Outils interactifs (jobs batch, scripts dev) atteignent EMFILE ; les démons système non.
Cause racine : Les limites PAM et les sessions utilisateur ont des valeurs par défaut basses ; les unités systemd système sont réglées séparément.
Correction : Ajustez les limites PAM (/etc/security/limits.conf ou drop-ins dans /etc/security/limits.d) et/ou les valeurs par défaut du gestionnaire utilisateur dans /etc/systemd/user.conf. Vérifiez dans une session fraîche.
5) « Nous avons mis LimitNOFILE à 2 millions et systemd a refusé »
Symptôme : Le service refuse de démarrer, ou la limite est plafonnée à un nombre plus bas.
Cause racine : Le noyau fs.nr_open est plus bas. RLIMIT_NOFILE ne peut pas dépasser cela.
Correction : Augmentez fs.nr_open via sysctl (avec précaution), puis définissez une limite sensée. Validez aussi la marge mémoire.
6) « Ça plante seulement après la rotation des logs »
Symptôme : Après rotation, le compte FD augmente ; finalement EMFILE apparaît.
Cause racine : L’application garde des anciens descripteurs de log ouverts (ou un process helper le fait). Ce n’est pas une fuite classique, mais ça y ressemble.
Correction : Assurez la bonne réouverture des logs (signal HUP quand approprié), ou utilisez stdout/stderr vers journald quand possible. Vérifiez les cibles FD dans /proc/<pid>/fd.
Listes de contrôle / plan étape par étape
Checklist A : corriger proprement un seul service systemd
- Confirmer EMFILE vs ENFILE dans les logs/journal.
- Obtenir le MainPID de l’unité :
systemctl show -p MainPID --value your.service. - Lire
/proc/<pid>/limits, noter le « Max open files » actuel. - Créer un drop-in :
systemctl edit your.serviceet définirLimitNOFILE=. systemctl daemon-reload, puis redémarrer le service.- Re-vérifier
/proc/<pid>/limitssur le nouveau PID. - Compter les FDs et identifier ce qu’ils sont (
/proc/<pid>/fd). - Si l’usage FD augmente à charge stable, traitez-le comme une fuite jusqu’à preuve du contraire.
Checklist B : décider du bon nombre (au lieu de « le rendre énorme »)
- Estimer la concurrence de pointe : connexions, fichiers, pipes, watchers.
- Ajouter une marge, mais garder une marge de sécurité pour comportement runaway (ne pas définir « infini »).
- Vérifier que les plafonds du noyau (
fs.nr_open) sont supérieurs à votre cible. - Considérer l’impact mémoire de nombreux sockets et buffers. Les nombres FD ne sont pas gratuits.
- Test de charge et surveillance : compte FD, mémoire, latence, taux d’erreur.
Checklist C : politique de flotte (si vous devez toucher aux valeurs par défaut)
- Documenter pourquoi vous avez besoin d’un changement par défaut et quels services en bénéficient.
- Définir
DefaultLimitNOFILEde manière conservative. Utiliser des overrides par service pour les cas particuliers. - Déployer progressivement et surveiller de nouveaux modes de défaillance (mémoire, latence).
- Standardiser la vérification : propriété d’unité + /proc limits + tendance du compte FD.
Faits intéressants et contexte historique
- Fait 1 : L’idée originelle d’Unix « tout est un fichier » a fait des descripteurs de fichiers la poignée universelle — excellente pour la composabilité, parfois responsable d’incidents.
- Fait 2 : Les premiers Unix avaient des limites FD faibles (souvent 64 ou 256). Beaucoup de valeurs par défaut sont restées conservatrices pendant des décennies pour protéger les machines partagées.
- Fait 3 : RLIMIT_NOFILE a des valeurs « soft » et « hard » ; la limite soft est celle qui déclenche EMFILE la plupart du temps.
- Fait 4 : systemd n’a pas seulement remplacé les scripts init ; il a standardisé la façon dont les services héritent des limites et de l’appartenance cgroup, rendant le comportement plus prévisible — une fois que vous savez où regarder.
- Fait 5 :
/proc/<pid>/limitsest l’une des sources de vérité les plus simples sur Linux. Elle est aussi dramatiquement sous-utilisée en réponse aux incidents. - Fait 6 : « Too many open files » concerne souvent des sockets, pas des fichiers. Les démons réseau à haute QPS peuvent le rencontrer sans toucher au disque.
- Fait 7 : Augmenter les limites FD peut augmenter substantiellement l’utilisation mémoire du noyau car sockets et structures epoll/watch ont un overhead par objet.
- Fait 8 : Certains services ont des plafonds internes (limites de workers, pools de connexion) qui doivent être alignés avec les limites OS ou vous entrez dans une boucle « j’ai corrigé l’OS, c’est toujours cassé ».
FAQ
1) Pourquoi ulimit -n ne corrige-t-il pas mon service systemd ?
Parce que votre service systemd n’est pas un enfant de votre shell interactif. systemd fixe les rlimits au démarrage du service selon l’unité et les valeurs par défaut de systemd. Le ulimit de votre shell n’affecte que les processus lancés depuis ce shell.
2) Est-ce que /etc/security/limits.conf est ignoré sur Debian 13 ?
Non. Il s’applique là où PAM s’applique : sessions de connexion (ssh, console, certains chemins su/sudo selon la config). Beaucoup de services système ne passent pas par PAM, donc ils n’hériteront pas de ces limites.
3) Dois‑je définir DefaultLimitNOFILE globalement ?
Généralement non. Définissez LimitNOFILE par service via des drop-ins. Les valeurs par défaut globales ne sont raisonnables que si vous imposez une baseline de flotte et comprenez les effets secondaires.
4) Quel nombre devrais‑je choisir pour LimitNOFILE ?
Choisissez en fonction du pic FD mesuré plus une marge, pas de la superstition. Pour des proxies chargés, des dizaines à centaines de milliers peuvent être normaux. Pour beaucoup d’apps, 8192–65536 suffit. Testez la charge et surveillez /proc/<pid>/fd.
5) Puis‑je augmenter LimitNOFILE sans redémarrer le service ?
Non. RLIMIT_NOFILE est défini par processus. Vous devez redémarrer (ou re‑exec) le processus pour appliquer une nouvelle limite. systemd ne peut pas changer rétroactivement le rlimit d’un processus en cours.
6) J’ai augmenté LimitNOFILE, mais les erreurs persistent. Et maintenant ?
Vérifiez que vous avez changé le processus en échec (vérifiez les workers). Inspectez ensuite l’usage FD et les types. Si les FDs augmentent régulièrement, vous avez probablement une fuite. Si cela pique avec le trafic, vous avez peut‑être besoin de mieux contrôler la concurrence ou d’ajuster des limites internes à l’application.
7) Quelle est la différence entre EMFILE et ENFILE déjà ?
EMFILE est par processus : ce processus a atteint RLIMIT_NOFILE. ENFILE est système‑wide : la table de fichiers de l’hôte est épuisée. La plupart des incidents « Too many open files » sur des services sont des EMFILE.
8) Les conteneurs changent-ils cette histoire ?
Oui, mais le principe reste : le processus a un vrai RLIMIT. Les runtimes de conteneurs peuvent le définir ; systemd à l’intérieur d’un conteneur peut le définir ; l’hôte peut le contraindre. Vérifiez toujours avec /proc/<pid>/limits dans le namespace/environnement pertinent.
9) Une limite FD élevée peut‑elle être un risque de sécurité ?
Elle peut être un risque de disponibilité. Si un service compromis ou bogué peut ouvrir des millions de FDs, il peut épuiser les ressources du noyau et dégrader l’hôte. Les limites font partie du contrôle du rayon d’impact.
Conclusion : prochaines étapes que vous pouvez faire aujourd’hui
Corriger « Too many open files » sur Debian 13 n’est pas une question de crier ulimit plus fort. Il s’agit de fixer la limite là où le service naît : systemd. Utilisez un drop-in par service avec LimitNOFILE. Rechargez. Redémarrez. Vérifiez dans /proc/<pid>/limits. Puis mesurez l’usage des FDs pour savoir si vous avez réglé un problème de capacité ou simplement repoussé une fuite.
Prochaines étapes pratiques :
- Choisissez un service qui a déjà lancé EMFILE, et ajoutez un drop-in avec un
LimitNOFILEsensé. - Créez un petit runbook : MainPID → /proc limits → compte FD.
- Pendant votre prochain test de charge, enregistrez le pic FD et fixez les limites sur ces données, pas sur le folklore.
Si vous faites cela une fois, vous cesserez de traiter les descripteurs de fichiers comme des gremlins mystérieux et vous commencerez à les considérer pour ce qu’ils sont : un budget. Et les budgets, hélas, comptent toujours.