Disque plein : trouver et libérer de l'espace sur un serveur Linux
Quand df annonce 100 % et que du ne trouve rien : la démarche complète, de la mesure à la libération d'espace, pour Debian 12 et 13 ainsi qu'Ubuntu 22.04 et 24.04.
Un volume de stockage saturé prévient rarement poliment. En général, un service tombe en premier : MariaDB écrit OS error 28 dans son log, nginx répond par une erreur 500, une sauvegarde s'interrompt sur write error: No space left on device, et dans le pire des cas plus personne n'accède à la machine en SSH, parce que sshd ne parvient plus à créer ses fichiers de session. Cet article déroule la démarche complète : mesurer, trouver, libérer, contrôler. Y compris les deux cas devant lesquels la plupart des tutoriels s'arrêtent, à savoir quand df annonce un disque plein et que du ne trouve rien.
Commencer par mesurer : quelle partition est réellement pleine ?
Avant de supprimer quoi que ce soit, il faut savoir quel système de fichiers est concerné. Un /boot plein n'a rien à voir avec un /var plein, les causes sont totalement différentes.
df -h
df -hT -x tmpfs -x devtmpfs -x squashfs
La seconde ligne masque les pseudo systèmes de fichiers. C'est particulièrement utile sur Ubuntu, où chaque application Snap installée apparaît comme un montage loop squashfs distinct et noie la sortie. Ces montages loop sont d'ailleurs toujours occupés à 100 %, c'est normal et sans conséquence.
La colonne qui compte est Mounted on. Les configurations typiques sur un serveur :
| Point de montage | Cause habituelle quand c'est plein |
|---|---|
| / | logs, Docker, cache apt, données applicatives |
| /boot | anciens noyaux et leurs images initramfs |
| /var | journal, rsyslog, file d'attente mail, bases de données, Docker |
| /tmp | uploads interrompus, sessions, restes de build |
Un détail sème souvent la confusion : par défaut, ext4 réserve cinq pour cent de la capacité à l'utilisateur root. Un service qui tourne sous www-data ou mysql reçoit donc déjà un No space left on device alors que root, à la même seconde, écrit encore sans le moindre problème. Sur des partitions de données pures (donc pas sur le système de fichiers racine), cette réserve peut être abaissée sans risque :
tune2fs -m 1 /dev/sdb1
Sur /, mieux vaut la conserver. C'est précisément la marge qui permet encore de réparer un système saturé.
Utiliser du correctement, au lieu de se perdre
L'approche classique consiste à descendre un niveau après l'autre, toujours avec -x :
du -xh --max-depth=1 / | sort -h
du -xh --max-depth=1 /var | sort -h
Le -x est l'option la plus importante de tout cet article. Elle maintient du à l'intérieur d'un seul système de fichiers et l'empêche de plonger dans /proc, /sys, des partages réseau montés ou des disques de sauvegarde. Sans -x, la recherche dure plusieurs minutes et renvoie des chiffres qui n'ont aucun rapport avec la partition pleine. sort -h trie correctement les tailles lisibles par un humain, si bien que le plus gros morceau se retrouve tout en bas.
Si vous préférez naviguer de façon interactive, installez ncdu et lancez-le lui aussi avec -x :
apt-get install -y ncdu
ncdu -x /
Les gros fichiers isolés se trouvent plus vite directement :
find /var -xdev -type f -size +100M -exec ls -lh {} +
Deux pièges mènent régulièrement à de mauvaises conclusions. Premièrement, du compte les blocs occupés, pas la taille logique du fichier. Sur les fichiers creux (tablespaces de bases de données, images de disques virtuels), les deux valeurs divergent fortement. La comparaison le rend visible :
du -sh /var/log
du --apparent-size -sh /var/log
Deuxièmement, du ne compte les liens physiques qu'une seule fois. En travaillant sans les droits root, vous obtenez en plus des lignes comme du: cannot read directory '/var/lib/private': Permission denied, et donc des totaux systématiquement trop bas. Menez toutes les analyses en root ou avec sudo.
Limiter le journal systemd
Sur un serveur, le journal est le dévoreur d'espace silencieux le plus fréquent. Par défaut, il peut occuper dix pour cent du système de fichiers, plafonné à quatre gigaoctets, et il garde en plus 15 % du système de fichiers libres. Sur un stockage de 500 gigaoctets, cela représente jusqu'à quatre gigaoctets de log pur.
journalctl --disk-usage
Il existe ici une vraie différence entre distributions, que beaucoup de tutoriels passent sous silence. Avec le réglage par défaut Storage=auto, le fait que le journal atterrisse ou non sur le stockage dépend uniquement de l'existence du répertoire /var/log/journal :
ls -d /var/log/journal
ls -d /run/log/journal
Sur les installations Debian minimales et sur beaucoup d'images cloud de Debian 12 et Debian 13, /var/log/journal n'existe pas. Le journal se trouve alors sous /run/log/journal, donc en RAM : il disparaît à chaque redémarrage et ne pèse pas du tout sur le stockage. En revanche, il consomme de la RAM. Ubuntu Server 22.04 et 24.04 créent en général ce répertoire et écrivent le journal en permanence sur le disque. Vérifiez plutôt que de deviner.
Pour libérer de la place immédiatement :
journalctl --rotate
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d
L'appel préalable à --rotate n'est pas décoratif : les options vacuum ne suppriment que les fichiers de journal déjà archivés, jamais celui qui est actif. Si le fichier actif représente l'essentiel du volume, il ne se passe apparemment rien tant que la rotation n'a pas eu lieu, et c'est exactement là que trébuchent les lecteurs qui copient la commande depuis un message de forum.
Pour une limite durable, passez par un fichier dédié, afin qu'une mise à jour de paquet ultérieure n'écrase rien :
mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=200M\nRuntimeMaxUse=50M\n' > /etc/systemd/journald.conf.d/00-size.conf
systemctl restart systemd-journald
Vérification que cela a réellement produit son effet : journalctl --disk-usage doit maintenant annoncer une valeur plus faible, et df -h doit montrer davantage d'espace libre. Si la taille du journal diminue alors que df reste inchangé, c'est qu'un processus garde encore des fichiers supprimés ouverts. Nous y revenons plus bas.
Deuxième différence entre distributions : sur les installations serveur classiques de Debian et d'Ubuntu, rsyslog tourne souvent en parallèle et écrit les mêmes messages une seconde fois dans /var/log/syslog. Sur les images Ubuntu minimales, prévues pour le cloud ou pour les conteneurs (containers), rsyslog est en revanche absent. Vérifiez avec ls -l /var/log/syslog. Si le fichier existe et qu'il est énorme, le problème ne vient pas du journal mais d'une règle logrotate manquante ou cassée sous /etc/logrotate.d/.
Cache apt et anciens noyaux
Les paquets téléchargés restent en place après l'installation. Sur un serveur en service depuis longtemps, cela atteint vite plusieurs gigaoctets.
du -sh /var/cache/apt
apt-get clean
du -sh /var/lib/apt/lists
apt-get clean vide complètement /var/cache/apt/archives, alors que apt-get autoclean ne retire que les paquets qui n'existent plus dans les dépôts. Ce à quoi clean ne touche pas, ce sont les listes de paquets sous /var/lib/apt/lists. Avec beaucoup de dépôts activés, elles peuvent atteindre plusieurs centaines de mégaoctets et se reconstruisent sans risque :
rm -rf /var/lib/apt/lists/*
apt-get update
La seconde ligne n'est pas une option, c'est une obligation, et tout de suite. Entre la suppression des listes et le prochain apt-get update, apt ne connaît plus un seul paquet : dans cet état, chaque apt-get install échoue avec E: Unable to locate package ... et un code de sortie 100, alors que le paquet est évidemment bien présent dans les dépôts.
Le deuxième grand classique, ce sont les anciens noyaux. Via unattended-upgrades, Ubuntu installe en continu de nouveaux noyaux, mais ne nettoie les anciens que si vous l'autorisez explicitement. Chaque noyau occupe, initramfs compris, environ 100 à 150 mégaoctets dans un /boot qui ne fait souvent que 512 mégaoctets à un gigaoctet.
uname -r
dpkg -l 'linux-image-*'
apt-get autoremove --purge
Le noyau en cours d'exécution, celui que renvoie uname -r, n'est jamais supprimé, pas plus que le plus récent. Sur Ubuntu, on prend les devants en ajoutant la ligne Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; dans /etc/apt/apt.conf.d/50unattended-upgrades. Sur Debian 12 et 13, unattended-upgrades n'est pas actif par défaut : /boot n'y grossit que si quelqu'un met à jour régulièrement à la main sans jamais faire le ménage.
Quand /boot est déjà plein et qu'apt ne passe plus
C'est le cas qui fait vraiment mal. Voici les messages typiques, mot pour mot :
update-initramfs: failed for /boot/initrd.img-6.8.0-60-generic with 1.
dpkg: error processing package linux-image-6.8.0-60-generic (--configure):
installed linux-image-6.8.0-60-generic package post-installation script subprocess returned error exit status 1
E: Sub-process /usr/bin/dpkg returned an error code (1)
La base de données des paquets se retrouve alors à moitié configurée, et tout appel apt suivant échoue au même endroit. La sortie de secours, dans cet ordre :
- Notez le résultat de
uname -r. Cette version ne doit être touchée sous aucun prétexte. - Affichez
ls -lh /bootet repérez la version la plus ancienne qui n'est pas en cours d'exécution. - Supprimez uniquement son
initrd.img-*, pas levmlinuz-*. Le fichier initramfs est de loin le plus volumineux et se régénère à tout moment. - Lancez
apt-get -f installpour que dpkg puisse terminer la configuration interrompue. - Seulement ensuite
apt-get autoremove --purge, afin que les anciens paquets disparaissent proprement, entrée GRUB comprise. - Exécutez
update-grubet lisez la sortie.
Ce qu'il ne faut pas faire : supprimer au hasard des fichiers de noyau dans /boot avec rm et ignorer le reste. dpkg continue alors de croire que les paquets sont installés, GRUB propose des entrées qui ne mènent nulle part, et le redémarrage suivant se termine dans l'invite de secours de GRUB. Si vous avez déjà supprimé, rétablissez la cohérence avec apt-get install --reinstall du paquet concerné ou avec dpkg --purge, puis impérativement update-grub.
Vérification : df -h /boot affiche de nouveau de l'espace libre, dpkg -l 'linux-image-*' ne liste plus que deux ou trois entrées à l'état ii, et la sortie de update-grub nomme exactement les noyaux réellement présents dans /boot.
Docker, Snap et logs de conteneurs
Sur un hôte Docker, la réponse se trouve presque toujours dans /var/lib/docker. Ne devinez pas, demandez :
docker system df
docker system df -v
La variante détaillée sépare proprement les images, les conteneurs, les volumes et le cache de build. Ensuite, faites le ménage de façon ciblée :
docker image prune -a
docker builder prune
docker system prune -a
Un mot de prudence à propos de --volumes : cette option supprime aussi les volumes auxquels aucun conteneur en cours d'exécution n'est rattaché. Si vous faites tourner votre base de données dans un volume nommé et que vous venez d'arrêter le conteneur, vous perdez les données. Sans sauvegarde récente, docker system prune -a --volumes n'a rien à faire sur un serveur de production.
Le poste sous-estimé, ce sont les logs de conteneurs sous /var/lib/docker/containers/*/*-json.log. Le pilote par défaut json-file n'effectue aucune rotation tant qu'on ne le lui demande pas explicitement. Un conteneur bavard écrit ainsi, en quelques mois, plusieurs dizaines de gigaoctets dans un seul fichier. Le remède, dans /etc/docker/daemon.json :
{
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "3" }
}
Ensuite systemctl restart docker. Attention, et cela ne figure quasiment dans aucun tutoriel : le réglage ne s'applique qu'aux conteneurs nouvellement créés. Les conteneurs existants conservent leur ancienne configuration jusqu'à ce qu'ils soient recréés une fois, donc avec Compose via docker compose up -d --force-recreate.
Les versions de Docker diffèrent nettement sur ce point. Debian 12 livre docker.io 20.10, Debian 13 apporte la 26.1, Ubuntu 22.04 et 24.04 en sont désormais à la 29.1. Plus la version est récente, plus le cache BuildKit occupe de place, et plus docker builder prune devient important, car docker system prune seul ne le vide pas toujours entièrement.
Sur Ubuntu, Snap s'ajoute au tableau. Les anciennes révisions restent en place, désactivées, et continuent d'occuper de l'espace :
snap list --all
snap set system refresh.retain=2
La valeur 2 est le minimum accepté par snapd, toute valeur inférieure est rejetée avec un message d'erreur. Debian n'embarque pas Snap, cette section n'y a tout simplement pas lieu d'être.
df annonce un disque plein, du ne trouve rien
Voici le cas intéressant. df -h affiche 100 %, mais le total de du n'en donne que la moitié. Il existe exactement quatre explications plausibles.
Des fichiers supprimés mais encore ouverts
C'est de loin la cause la plus fréquente. Quelqu'un a exécuté rm /var/log/riesig.log alors qu'un service gardait encore le fichier ouvert. L'entrée de répertoire a disparu, c'est pourquoi du ne voit plus rien. Les blocs restent occupés jusqu'à la fermeture du dernier descripteur de fichier, c'est pourquoi df les voit toujours.
apt-get install -y lsof
lsof +L1
La colonne NLINK affiche alors un 0, et (deleted) apparaît derrière le chemin. Si lsof ne trouve rien, il n'y a aucune sortie et le code de retour vaut 1, ce n'est pas une erreur. Sans lsof, cela fonctionne aussi directement via le système de fichiers des processus :
ls -l /proc/*/fd 2>/dev/null | grep deleted
Important : exécutez impérativement en root. En simple utilisateur, vous ne voyez que vos propres descripteurs et vous manquez précisément les services système, responsables dans neuf cas sur dix.
La méthode propre pour libérer l'espace consiste à redémarrer le service, par exemple systemctl restart rsyslog. Si un redémarrage est exclu, le fichier peut être tronqué à une longueur nulle via son descripteur. L'identifiant du processus et le numéro de descripteur proviennent de la sortie de lsof :
truncate -s 0 /proc/1234/fd/7
Les blocs sont libérés immédiatement et le processus continue d'écrire ensuite. Cette méthode est réservée aux simples fichiers de log ouverts en mode ajout. Ne l'appliquez jamais à des fichiers de bases de données, à des images de machines virtuelles ou à quoi que ce soit d'autre en accès aléatoire : elle y provoque une perte de données.
Comment savoir que cela a fonctionné : df -h affiche aussitôt plus d'espace libre, et lsof +L1 ne liste plus l'entrée. Un second passage de du, en revanche, ne change absolument rien, puisque le fichier y était déjà invisible auparavant. C'est aussi exactement la raison pour laquelle un redémarrage du serveur semble résoudre le problème comme par magie.
Des fichiers masqués sous un point de montage
Un grand classique : quelqu'un a écrit des données dans /mnt/backup avant que le disque prévu n'y soit monté. Les données se trouvent toujours sur le système de fichiers racine, mais elles sont masquées par le montage placé par-dessus. Cela ne devient visible qu'à travers une seconde vue du même système de fichiers :
mkdir -p /mnt/rootview
mount --bind / /mnt/rootview
du -xh --max-depth=2 /mnt/rootview | sort -h
umount /mnt/rootview
Le montage bind est sans danger, il ne déplace aucun montage et se retire avec umount.
La réserve root et l'erreur de droits
Les cinq pour cent de réserve ext4 déjà évoqués expliquent l'écart entre « pas encore tout à fait plein » et « les services échouent déjà ». Enfin, qui lance du sans les droits root ne voit pas des arborescences entières. La différence supposée n'est alors qu'un problème de droits. Sur btrfs et ZFS, les snapshots entrent en plus en ligne de compte, et du ne les affiche jamais non plus.
Quand ce ne sont pas les octets qui manquent, mais les inodes
Il existe une seconde forme de « plein » qui produit exactement le même message d'erreur. Chaque fichier et chaque répertoire occupe un inode, et sur ext4 leur nombre est figé depuis le moment du formatage.
df -i
stat -f /
Si la colonne IUse% de df -i est à 100 alors que df -h annonce beaucoup d'espace libre, le diagnostic est sans ambiguïté : le serveur n'a pas un problème de place, il a un problème de nombre. Des millions de fichiers minuscules ont consommé tous les inodes. Le message d'erreur reste pourtant No space left on device, et c'est précisément pour cela que la plupart des gens cherchent au mauvais endroit.
On trouve les responsables en comptant les fichiers plutôt que les octets :
find /var -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -20
Les candidats fréquents sont la file d'attente mail sous /var/spool/postfix ou /var/spool/exim4, les sessions PHP sous /var/lib/php/sessions, les répertoires de cache des applications web, les dépendances Node déposées sur le disque et un /tmp jamais vidé.
La suppression elle-même réserve l'obstacle suivant. Avec un très grand nombre de fichiers, rm /var/lib/php/sessions/* échoue sur bash: /usr/bin/rm: Argument list too long, parce que la ligne de commande dépasse la limite de taille. La méthode qui fonctionne toujours :
find /var/lib/php/sessions -type f -mtime +7 -delete
Ce qu'il faut savoir : le nombre d'inodes d'un système de fichiers ext4 existant ne peut pas être augmenté après coup. La seule solution consiste à agrandir le système de fichiers (les inodes croissent alors proportionnellement) ou à le recréer avec une densité plus élevée, par exemple avec mkfs.ext4 -i 8192 /dev/sdb1. Si vous savez d'avance que vous travaillerez avec énormément de petits fichiers, par exemple sur un serveur mail ou une photothèque, XFS vous servira mieux : XFS crée les inodes dynamiquement et n'en manque pratiquement que lorsque la place vient elle aussi à manquer. Debian et Ubuntu formatent par défaut en ext4, XFS doit être choisi délibérément.
Contrôler et faire en sorte que cela ne revienne pas
Un nettoyage n'est réussi que lorsque trois éléments concordent : df -h montre plus d'espace libre, df -i montre une occupation d'inodes en baisse, et le service qui était tombé au départ fonctionne de nouveau. Une commande qui s'est déroulée sans message d'erreur ne prouve rien à elle seule. journalctl --vacuum-size et docker system prune, en particulier, ne font volontiers rien du tout, et sans le dire.
Pour l'exploitation au quotidien, une courte liste fait ses preuves : limiter fermement le journal via SystemMaxUse, configurer la rotation des logs Docker dans daemon.json, activer Remove-Unused-Kernel-Packages sur Ubuntu, prendre en charge vos propres logs applicatifs via /etc/logrotate.d/ et mettre en place un simple job cron qui envoie un mail dès qu'un seuil est dépassé. Sur les serveurs KVM et les serveurs dédiés, il vaut également la peine de placer /var, ou au moins /var/log, sur une partition séparée. Un emballement des logs paralyse alors un service, mais pas le système d'exploitation, et vous gardez dans tous les cas l'accès SSH à la machine.
Questions fréquentes
Pourquoi df affiche-t-il 100 % alors que du trouve nettement moins ?
Quelle place le journal systemd peut-il occuper et comment le limiter durablement ?
Le journal est-il stocké au même endroit sur Debian et sur Ubuntu ?
/boot est plein et apt s'arrête sur une erreur dpkg. Que faire ?
La commande "docker system prune -a --volumes" est-elle sûre ?
Que faire quand df -i montre les inodes occupés à 100 % ?
2026 KernelHost GmbH. Tous droits réservés. Ce guide est protégé par le droit d'auteur. Sa republication sur d'autres sites web, même partielle ou sous une forme modifiée, n'est pas autorisée sans notre accord écrit. Les citations accompagnées de la source et d'un lien sont expressément les bienvenues.

