Une erreur Too many open files (EMFILE) persiste souvent malgré une augmentation de ulimit -n dans /etc/security/limits.conf, un fichier qui semble être l’endroit évident pour ce réglage mais qui ne s’applique tout simplement pas à un service géré par systemd.

Ce que ulimit -n limite réellement

Chaque processus a un nombre maximal de descripteurs de fichiers ouverts simultanément (fichiers, sockets réseau, pipes), une limite fixée par le kernel et configurable via deux plafonds distincts : une limite souple (soft), modifiable par le processus lui-même dans cette limite, et une limite dure (hard), le plafond absolu qu’aucun processus non privilégié ne peut dépasser.

# Limite souple actuelle et limite dure maximale
# pour la session shell courante
ulimit -Sn
ulimit -Hn

/etc/security/limits.conf : uniquement pour les sessions PAM

limits.conf configure les limites via PAM (Pluggable Authentication Modules), un mécanisme qui s’applique aux sessions de connexion classiques (SSH, console locale, su, sudo), mais jamais aux services démarrés directement par systemd, qui ne passent par aucune session PAM.

# /etc/security/limits.conf : n'a strictement aucun effet
# sur un service démarré par systemd
myuser soft nofile 65536
myuser hard nofile 65536

Un service systemd lancé au démarrage du système, sans jamais passer par une connexion utilisateur, ignore entièrement ce fichier : augmenter cette limite peut résoudre une erreur pour une session shell interactive, sans jamais affecter le service qui continue de tourner avec la limite par défaut du système (souvent 1024).

LimitNOFILE= : le seul réglage qui compte pour systemd

Un service systemd hérite de sa propre limite, configurée directement dans son unité via LimitNOFILE=, indépendamment de tout ce qui est défini dans limits.conf.

# Dans le fichier .service, la seule ligne qui affecte
# réellement un service géré par systemd
[Service]
LimitNOFILE=65536
# Vérifier la limite effective d'un service en cours
# d'exécution, la seule vérification fiable
cat /proc/$(systemctl show -p MainPID --value my-app)/limits | grep "open files"

Sans ce réglage explicite dans l’unité, un service hérite d’une valeur par défaut (souvent 1024, parfois plus selon la distribution et sa configuration système), une limite atteinte rapidement par une application qui ouvre beaucoup de connexions réseau simultanées ou qui manipule un grand nombre de fichiers en parallèle.

Le piège du diagnostic : deux vérifications, pas une

Vérifier ulimit -n depuis un shell interactif ne dit rien de la limite réelle appliquée à un service systemd en cours d’exécution : les deux contextes sont entièrement séparés, chacun avec sa propre source de configuration. Confondre les deux mène à conclure à tort qu’une augmentation dans limits.conf a résolu le problème, alors que le service continue de fonctionner sous sa limite systemd par défaut, inchangée.

À retenir

/etc/security/limits.conf configure les limites via PAM, un mécanisme qui ne s’applique qu’aux sessions de connexion classiques, jamais à un service démarré directement par systemd. LimitNOFILE= dans le fichier .service est le seul réglage qui compte pour un tel service, totalement indépendant de limits.conf. Vérifier la limite effective directement via /proc/<pid>/limits plutôt que de se fier à ulimit -n en session interactive évite de conclure à tort qu’un réglage a résolu un problème qui persiste en réalité, un des pièges classiques qui reste pertinent dès qu’une application tourne comme service système plutôt qu’en session interactive.