Schijf vol: opslagruimte op de Linux-server vinden en vrijmaken
Wanneer df 100% meldt en du niets vindt: de volledige route van meten tot vrijmaken, voor Debian 12 en 13 en voor Ubuntu 22.04 en 24.04.
Een volle schijf meldt zich zelden beleefd. Meestal valt eerst een dienst uit: MariaDB schrijft OS error 28 in het log, nginx antwoordt met een 500, een back-up breekt af met write error: No space left on device, en als het echt tegenzit komt niemand nog via SSH op de machine, omdat sshd geen sessiebestanden meer kan aanmaken. Dit artikel laat de volledige route zien: meten, vinden, vrijmaken, controleren. Inclusief de twee gevallen waarbij de meeste handleidingen ophouden, namelijk wanneer df vol meldt en du niets vindt.
Eerst meten: welke partitie is er eigenlijk vol?
Voordat er ook maar iets wordt verwijderd, moet duidelijk zijn welk bestandssysteem het betreft. Een volle /boot heeft heel andere oorzaken dan een volle /var.
df -h
df -hT -x tmpfs -x devtmpfs -x squashfs
De tweede regel verbergt de pseudobestandssystemen. Op Ubuntu is dat bijzonder nuttig, want daar duikt elke geïnstalleerde Snap-toepassing op als eigen squashfs-loop-mount en vervuilt zo de uitvoer. Die loop-mounts zijn overigens altijd voor 100% bezet, dat is normaal en geen probleem.
Belangrijk is de kolom Mounted on. Typische situaties op servers:
| Mountpoint | Gebruikelijke oorzaak als het vol is |
|---|---|
| / | logs, Docker, apt-cache, applicatiegegevens |
| /boot | oude kernels en hun initramfs-images |
| /var | journal, rsyslog, mailwachtrij, databases, Docker |
| /tmp | afgebroken uploads, sessies, buildresten |
Nog een detail dat vaak voor verwarring zorgt: ext4 reserveert standaard vijf procent van de capaciteit voor de gebruiker root. Een dienst die als www-data of mysql draait, krijgt dus al een No space left on device, terwijl root op datzelfde moment nog probleemloos kan schrijven. Op partities die alleen data bevatten (dus niet op het rootbestandssysteem) mag u die reserve zonder risico verlagen:
tune2fs -m 1 /dev/sdb1
Op / moet de reserve blijven staan. Dat is precies de buffer waarmee u een volgelopen systeem nog kunt repareren.
du goed inzetten in plaats van verdwalen
De klassieke aanpak werkt één niveau tegelijk, altijd met -x:
du -xh --max-depth=1 / | sort -h
du -xh --max-depth=1 /var | sort -h
De -x is de belangrijkste optie in dit hele artikel. Die houdt du binnen één bestandssysteem en voorkomt dat het commando afdaalt in /proc, /sys, aangekoppelde netwerkshares of back-upschijven. Zonder -x loopt de zoekactie minutenlang en levert getallen op die niets met de volle partitie te maken hebben. sort -h sorteert de leesbare groottes correct, zodat de grootste brok onderaan staat.
Klikt u liever interactief door de mappen, installeer dan ncdu en start dat eveneens met -x:
apt-get install -y ncdu
ncdu -x /
Losse grote bestanden vindt u sneller rechtstreeks:
find /var -xdev -type f -size +100M -exec ls -lh {} +
Twee valkuilen leiden hier regelmatig tot verkeerde conclusies. Ten eerste: du telt bezette blokken, niet de logische bestandsgrootte. Bij sparse bestanden (database-tablespaces, images van virtuele schijven) lopen beide waarden ver uiteen. De vergelijking maakt dat zichtbaar:
du -sh /var/log
du --apparent-size -sh /var/log
Ten tweede: du telt harde links maar één keer. Werkt u zonder root, dan krijgt u bovendien regels als du: cannot read directory '/var/lib/private': Permission denied en daarmee structureel te lage totalen. Voer alle analyses dus als root uit, of met sudo.
Het systemd-journal begrenzen
Op servers is het journal de meest voorkomende stille slokop van schijfruimte. Standaard mag het tien procent van het bestandssysteem gebruiken, met een plafond van vier gigabyte, en daarnaast houdt het 15% van het bestandssysteem vrij. Op een schijf van 500 gigabyte is dat tot vier gigabyte aan puur log.
journalctl --disk-usage
Hier zit een echt verschil tussen de distributies, dat veel handleidingen weglaten. Of het journal daadwerkelijk op de schijf terechtkomt, hangt bij de standaardinstelling Storage=auto uitsluitend af van de vraag of de map /var/log/journal bestaat:
ls -d /var/log/journal
ls -d /run/log/journal
Op minimale Debian-installaties en op veel cloud-images van Debian 12 en Debian 13 bestaat /var/log/journal niet. Het journal staat dan onder /run/log/journal, dus in RAM, verdwijnt bij elke herstart en belast de schijf helemaal niet. Het belast in plaats daarvan het werkgeheugen. Ubuntu Server 22.04 en 24.04 maken de map juist meestal wel aan en loggen permanent naar de schijf. Controleren in plaats van gokken.
Direct ruimte vrijmaken:
journalctl --rotate
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d
De aanroep van --rotate daarvoor is geen versiering: de vacuum-opties verwijderen uitsluitend journalbestanden die al gearchiveerd zijn, nooit het bestand dat op dat moment actief is. Als juist dat actieve bestand het grootste deel uitmaakt, lijkt er zonder voorafgaand roteren niets te gebeuren. Precies daarover struikelen lezers die het commando uit een forumbericht overnemen.
Voor een blijvende begrenzing gebruikt u een eigen bestand, zodat latere pakketupdates niets overschrijven:
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
Controle of het echt heeft gewerkt: journalctl --disk-usage moet nu een kleinere waarde melden en df -h moet meer vrije ruimte tonen. Krimpt het journal terwijl df onveranderd blijft, dan houdt een proces nog verwijderde bestanden open. Daarover verderop meer.
Tweede verschil tussen de distributies: op klassieke serverinstallaties van Debian en Ubuntu draait vaak ook rsyslog mee, dat dezelfde meldingen een tweede keer naar /var/log/syslog schrijft. Op minimale Ubuntu-images (cloud, container) ontbreekt rsyslog juist. Controleer dat met ls -l /var/log/syslog. Bestaat het bestand en is het enorm groot, dan is niet het journal het probleem, maar een ontbrekende of kapotte logrotate-regel onder /etc/logrotate.d/.
apt-cache en oude kernels
Gedownloade pakketten blijven na de installatie staan. Op een server die lang draait, is dat al snel meerdere gigabytes.
du -sh /var/cache/apt
apt-get clean
du -sh /var/lib/apt/lists
apt-get clean leegt /var/cache/apt/archives volledig, apt-get autoclean alleen de pakketten die niet meer in de pakketbronnen staan. Wat clean niet aanraakt, zijn de pakketlijsten onder /var/lib/apt/lists. Bij veel geconfigureerde pakketbronnen kunnen die enkele honderden megabytes bereiken en ze zijn zonder risico opnieuw op te bouwen:
rm -rf /var/lib/apt/lists/*
apt-get update
De tweede regel is daarbij geen vrije keuze maar een verplichting, en wel meteen. Tussen het verwijderen van de lijsten en de volgende apt-get update kent apt geen enkel pakket meer: elke apt-get install breekt in die toestand af met E: Unable to locate package ... en exitcode 100, ook al staat het pakket vanzelfsprekend gewoon in de pakketbronnen.
De tweede klassieker zijn oude kernels. Ubuntu installeert via unattended-upgrades doorlopend nieuwe kernels, maar ruimt de oude alleen op als u dat uitdrukkelijk toestaat. Elke kernel neemt samen met zijn initramfs ongeveer 100 tot 150 megabyte in beslag in een /boot dat vaak maar 512 megabyte tot één gigabyte groot is.
uname -r
dpkg -l 'linux-image-*'
apt-get autoremove --purge
De draaiende kernel uit uname -r wordt daarbij nooit verwijderd, en de nieuwste evenmin. Op Ubuntu voorkomt u het probleem door in /etc/apt/apt.conf.d/50unattended-upgrades de regel Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; te zetten. Op Debian 12 en 13 is unattended-upgrades standaard niet actief; daar groeit /boot alleen als iemand regelmatig met de hand bijwerkt en nooit opruimt.
Wanneer /boot al vol is en apt niet meer doorloopt
Dit is het geval dat echt pijn doet. Typische meldingen, letterlijk:
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)
Nu staat de pakketdatabase in een halfafgewerkte toestand en elke volgende apt-aanroep loopt op hetzelfde punt vast. De uitweg, in deze volgorde:
uname -rnoteren. Aan die versie wordt onder geen beding iets veranderd.ls -lh /bootuitvoeren en de oudste versie opzoeken die niet draait.- Alleen de bijbehorende
initrd.img-*verwijderen, niet devmlinuz-*. Het initramfs-bestand is verreweg het grootste en kan op elk moment opnieuw worden aangemaakt. apt-get -f installuitvoeren, zodat dpkg de afgebroken configuratie kan afronden.- Pas daarna
apt-get autoremove --purge, zodat de oude pakketten netjes en inclusief hun GRUB-vermelding verdwijnen. update-grublaten lopen en de uitvoer lezen.
Wat u niet moet doen: met rm lukraak kernelbestanden uit /boot verwijderen en de rest negeren. dpkg denkt dan nog steeds dat de pakketten geïnstalleerd zijn, GRUB biedt vermeldingen aan die nergens heen wijzen, en de volgende herstart eindigt in de reddingsprompt van GRUB. Hebt u al iets verwijderd, herstel de consistentie dan met apt-get install --reinstall van het betreffende pakket of met dpkg --purge, gevolgd door een verplichte update-grub.
Controle: df -h /boot toont weer vrije ruimte, dpkg -l 'linux-image-*' laat nog maar twee of drie regels met status ii zien, en de uitvoer van update-grub noemt precies de kernels die ook echt in /boot staan.
Docker, Snap en containerlogs
Op Docker-hosts ligt het antwoord bijna altijd in /var/lib/docker. Niet gokken, maar navragen:
docker system df
docker system df -v
De uitgebreide variant splitst netjes uit naar images, containers, volumes en buildcache. Daarna gericht opruimen:
docker image prune -a
docker builder prune
docker system prune -a
Een woord van waarschuwing bij --volumes: die optie verwijdert ook volumes waaraan geen draaiende container hangt. Draait uw database in een benoemd volume en hebt u de container net gestopt, dan bent u de data kwijt. Zonder actuele back-up hoort docker system prune -a --volumes niet thuis op een productieserver.
De onderschatte post zijn de containerlogs onder /var/lib/docker/containers/*/*-json.log. De standaarddriver json-file roteert zonder uitdrukkelijke instelling helemaal niet. Een spraakzame container schrijft zo in de loop van maanden tientallen gigabytes naar één enkel bestand. Het tegengif komt in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "3" }
}
Daarna systemctl restart docker. Let op, en dat staat in vrijwel geen enkele handleiding: de instelling geldt alleen voor nieuw aangemaakte containers. Bestaande containers houden hun oude configuratie tot ze één keer opnieuw worden aangemaakt, bij Compose dus via docker compose up -d --force-recreate.
De Docker-versies verschillen hier duidelijk. Debian 12 levert docker.io 20.10, Debian 13 komt met 26.1, Ubuntu 22.04 en 24.04 zitten inmiddels op 29.1. Hoe nieuwer de versie, hoe meer ruimte de BuildKit-cache inneemt en hoe belangrijker docker builder prune wordt, want docker system prune alleen leegt die niet altijd volledig.
Op Ubuntu komt Snap daar nog bij. Oude revisies blijven uitgeschakeld staan en nemen gewoon ruimte in beslag:
snap list --all
snap set system refresh.retain=2
De waarde 2 is het minimum dat snapd accepteert, kleinere waarden worden met een foutmelding geweigerd. Debian heeft geen Snap aan boord, daar vervalt dit onderdeel volledig.
df meldt vol, du vindt niets
Nu het interessante geval. df -h toont 100%, maar het totaal uit du komt niet verder dan de helft. Daarvoor zijn er precies vier plausibele oorzaken.
Verwijderde bestanden die nog geopend zijn
Dit is verreweg de meest voorkomende oorzaak. Iemand heeft rm /var/log/riesig.log uitgevoerd terwijl een dienst het bestand nog open had staan. De vermelding in de map is verdwenen, daarom ziet du niets meer. De blokken blijven bezet tot de laatste bestandsdescriptor is gesloten, daarom ziet df ze nog wel.
apt-get install -y lsof
lsof +L1
In de kolom NLINK staat dan een 0 en achter het pad staat (deleted). Vindt lsof niets, dan is er geen uitvoer en is de retourwaarde 1; dat is geen fout. Zonder lsof kan het ook rechtstreeks via het procesbestandssysteem:
ls -l /proc/*/fd 2>/dev/null | grep deleted
Belangrijk: voer dit beslist als root uit. Als gewone gebruiker ziet u alleen uw eigen descriptors en mist u juist die systeemdiensten die in negen van de tien gevallen de veroorzaker zijn.
De nette manier om de ruimte vrij te geven is een herstart van de dienst, bijvoorbeeld systemctl restart rsyslog. Komt een herstart niet in aanmerking, dan kunt u het bestand via zijn descriptor tot lengte nul inkorten. Proces-ID en descriptornummer komen uit de uitvoer van lsof:
truncate -s 0 /proc/1234/fd/7
Dat geeft de blokken meteen vrij en het proces schrijft daarna gewoon door. Deze methode is bedoeld voor zuivere logbestanden die in append-modus geopend zijn. Op databasebestanden, images van virtuele machines of alles wat verder willekeurige toegang gebruikt, mag u deze methode nooit toepassen, daar leidt ze tot dataverlies.
Waaraan u ziet dat het gelukt is: df -h toont meteen meer vrije ruimte en lsof +L1 toont de regel niet meer. Een tweede ronde van du verandert daarentegen helemaal niet, want daar was het bestand toch al onzichtbaar. Precies daarom lijkt een herstart van de server het probleem op magische wijze op te lossen.
Bestanden die onder een mountpoint verborgen zitten
Een klassieker: iemand heeft data naar /mnt/backup geschreven voordat de eigenlijke schijf daar was aangekoppeld. Die data staat nog steeds op het rootbestandssysteem, maar wordt verborgen door de mount die eroverheen ligt. Zichtbaar wordt dat alleen via een tweede weergave van hetzelfde bestandssysteem:
mkdir -p /mnt/rootview
mount --bind / /mnt/rootview
du -xh --max-depth=2 /mnt/rootview | sort -h
umount /mnt/rootview
De bind-mount is ongevaarlijk, hij verplaatst niets en wordt met umount weer verwijderd.
De root-reserve en de rechtenfout
De al genoemde vijf procent ext4-reserve verklaart het gat tussen "nog niet helemaal vol" en "diensten vallen nu al uit". En tot slot: wie du zonder root start, ziet hele directorystructuren niet. Het vermeende verschil is dan simpelweg een rechtenprobleem. Op btrfs en ZFS komen daar snapshots als verklaring bij, die du eveneens nooit toont.
Wanneer niet de ruimte opraakt, maar de inodes
Er bestaat een tweede soort "vol" die exact dezelfde foutmelding oplevert. Elk bestand en elke map bezet een inode, en bij ext4 ligt dat aantal vast vanaf het moment van formatteren.
df -i
stat -f /
Staat in df -i de kolom IUse% op 100 terwijl df -h ruim voldoende vrije ruimte meldt, dan is de diagnose duidelijk: de server heeft geen gebrek aan ruimte, het aantal mogelijke bestanden is op. Miljoenen piepkleine bestanden hebben alle inodes verbruikt. De foutmelding luidt desondanks No space left on device, en juist daarom zoeken de meeste mensen op de verkeerde plek.
De veroorzakers vindt u door bestanden te tellen in plaats van bytes:
find /var -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -20
Veelvoorkomende kandidaten zijn de mailwachtrij onder /var/spool/postfix of /var/spool/exim4, PHP-sessies onder /var/lib/php/sessions, cachemappen van webapplicaties, uitgedijde Node-afhankelijkheden en een /tmp die nooit wordt geleegd.
Bij het verwijderen zelf wacht de volgende horde. rm /var/lib/php/sessions/* mislukt bij heel veel bestanden met bash: /usr/bin/rm: Argument list too long, omdat de commandoregel de groottelimiet overschrijdt. De aanpak die altijd werkt:
find /var/lib/php/sessions -type f -mtime +7 -delete
Wat u moet weten: het aantal inodes van een bestaand ext4-bestandssysteem valt achteraf niet te verhogen. Het helpt alleen om het bestandssysteem te vergroten (daarbij groeit het aantal inodes evenredig mee) of om het opnieuw aan te maken met een dichtere bezetting, bijvoorbeeld met mkfs.ext4 -i 8192 /dev/sdb1. Wie naar verwachting met heel veel kleine bestanden werkt, bijvoorbeeld bij mailservers of beeldarchieven, is beter af met XFS, omdat XFS inodes dynamisch aanmaakt en praktisch pas opraakt wanneer ook de ruimte opraakt. Debian en Ubuntu formatteren standaard met ext4, XFS moet u bewust kiezen.
Controleren en zorgen dat het niet terugkomt
Een opruimactie geldt pas als geslaagd wanneer drie dingen kloppen: df -h toont meer vrije ruimte, df -i laat een dalende inodebezetting zien, en de dienst die oorspronkelijk uitviel, draait weer. Een commando dat zonder foutmelding is doorgelopen, is op zichzelf nog geen bewijs. Vooral journalctl --vacuum-size en docker system prune doen graag zonder enige melding precies niets.
Voor de dagelijkse praktijk bewijst een korte lijst zijn nut: het journal hard begrenzen met SystemMaxUse, de Docker-logrotatie in de daemon.json instellen, op Ubuntu Remove-Unused-Kernel-Packages inschakelen, eigen applicatielogs via /etc/logrotate.d/ meenemen en een eenvoudige cronjob inrichten die een mail stuurt zodra een drempel wordt overschreden. Op KVM-servers en dedicated servers loont het bovendien om /var, of op zijn minst /var/log, op een eigen partitie te zetten. Een log dat op hol slaat legt dan wel een dienst plat, maar niet het besturingssysteem, en u komt in elk geval nog via SSH op de machine.
Veelgestelde vragen
Waarom toont df 100% terwijl du duidelijk minder vindt?
Hoeveel ruimte mag het systemd-journal innemen en hoe begrens ik dat blijvend?
Staat het journal op Debian en Ubuntu op dezelfde plek?
/boot is vol en apt breekt af met een dpkg-fout. Wat nu?
Is "docker system prune -a --volumes" veilig?
Wat te doen als df -i de inodes voor 100% bezet toont?
2026 KernelHost GmbH. Alle rechten voorbehouden. Deze handleiding is auteursrechtelijk beschermd. Publicatie op andere websites, geheel, gedeeltelijk of in bewerkte vorm, is zonder onze schriftelijke toestemming niet toegestaan. Citeren met bronvermelding en link is uitdrukkelijk welkom.

