Un disque qui se remplit sans qu’aucun fichier visible via du n’explique l’espace consommé cache souvent le même piège : un fichier de log renommé par logrotate, mais toujours ouvert par le processus qui continue d’y écrire, invisible à toute exploration classique du système de fichiers.

Ce que logrotate fait par défaut : renommer, pas vider

Le mode par défaut de logrotate renomme le fichier de log actuel (app.log devient app.log.1) puis crée un nouveau fichier vide au nom d’origine. Cette approche suppose que le processus qui écrivait dans l’ancien fichier va, à un moment donné, fermer son descripteur de fichier et en ouvrir un nouveau vers le nom d’origine, ce qu’aucun processus ne fait automatiquement sans y être explicitement invité.

# rotate (comportement par défaut) :
app.log        → renommé en app.log.1
(nouveau)      → créé au nom app.log

Le piège : un descripteur de fichier ouvert survit au renommage

Sous Linux, renommer ou supprimer un fichier n’invalide jamais un descripteur de fichier déjà ouvert dessus : le processus continue d’écrire dans les mêmes blocs disque physiques, sous son ancien nom devenu invisible dans l’arborescence normale (app.log.1, ou même un fichier complètement supprimé si une rotation ultérieure l’a purgé). Le fichier app.log fraîchement créé par logrotate reste vide, pendant que l’espace disque réel continue de se remplir sous un nom que plus personne ne consulte.

# Révèle les fichiers supprimés mais toujours ouverts
# par un processus, invisibles à toute exploration normale
lsof +L1 | grep deleted
# app          1234  www-data   3w   REG   8,1  2147483648  app.log (deleted)

Un fichier supprimé de 2 Go toujours ouvert par un processus actif n’apparaît dans aucun du classique : seul lsof révèle cette classe de fuite d’espace disque, une commande rarement le premier réflexe face à un disque qui se remplit sans cause visible.

postrotate : signaler au processus de rouvrir son log

Le bloc postrotate de logrotate exécute une commande après la rotation, typiquement un signal envoyé au processus concerné pour qu’il ferme son ancien descripteur et en ouvre un nouveau vers le fichier fraîchement créé.

/var/log/app/*.log {
    daily
    rotate 7
    postrotate
        systemctl reload my-app
    endscript
}

Sans ce postrotate correctement configuré pour l’application concernée, chaque rotation aggrave le problème plutôt que de le résoudre : le nombre de descripteurs orphelins pointant vers des fichiers renommés (ou supprimés à la rotation suivante) grandit à chaque cycle.

copytruncate : l’alternative qui évite le problème par construction

copytruncate copie le contenu du fichier de log actuel vers l’archive, puis vide le fichier original sur place, sans jamais le renommer ni le remplacer. Le processus qui écrivait continue d’utiliser le même descripteur de fichier, vers le même nom, sans jamais se retrouver déconnecté de son fichier réel.

/var/log/app/*.log {
    daily
    rotate 7
    copytruncate
}

Cette approche élimine le besoin d’un postrotate applicatif, au prix d’un risque réel mais généralement mineur : une écriture qui survient exactement entre la copie et le vidage peut être perdue, une fenêtre de quelques millisecondes rarement significative pour un simple fichier de log.

À retenir

logrotate, en mode par défaut, renomme un fichier de log sans jamais garantir que le processus qui l’utilise ferme et rouvre son descripteur : un fichier renommé (ou supprimé) reste pleinement accessible en écriture pour le processus qui l’avait déjà ouvert, un espace disque consommé qu’aucun du classique ne révèle, seul lsof +L1 | grep deleted le fait. postrotate corrige le problème en signalant explicitement au processus de rouvrir son log ; copytruncate l’évite structurellement en ne renommant jamais le fichier, au prix d’une fenêtre de perte d’écriture minime. Ce piège explique une part non négligeable des disques qui se remplissent sans cause visible, une des vérifications à connaître dès qu’une infrastructure fait tourner des services qui écrivent leurs propres logs sur disque local.