Versteckte Speicherfresser in Sekunden finden
Eine Platte, die plötzlich voll läuft, legt Deployments und Datenbanken lahm, oft ohne Vorwarnung. Mit df findet ihr sofort, welches Dateisystem betroffen ist, mit du -sh sortiert nach Größe den genauen Verursacher, und mit dem passenden Handwerkszeug räumt ihr Logs, Caches und alte Releases zuverlässig auf, bevor der Server ausfällt.
Inhaltsverzeichnis
- 1. Warum df und du zusammengehören
- 2. df: Speicherplatz auf Dateisystem-Ebene messen
- 3. du: den Verursacher im Verzeichnisbaum finden
- 4. Wenn df voll zeigt, du aber nicht aufgeht
- 5. Log-Dateien: der häufigste Speicherfresser
- 6. Cache- und Session-Verzeichnisse aufräumen
- 7. Alte Releases, vendor-Verzeichnisse und Deployment-Artefakte
- 8. Automatisierte Aufräumroutinen und Monitoring
- 9. du und df im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum df und du zusammengehören
df und du beantworten zwei unterschiedliche Fragen, die auf den ersten Blick wie dieselbe klingen. df (disk free) liest die Belegungsstatistik direkt aus dem Superblock eines gemounteten Dateisystems aus und beantwortet die Frage: Wie voll ist diese Partition insgesamt? du (disk usage) dagegen durchläuft rekursiv einen Verzeichnisbaum und summiert die Größe jeder einzelnen Datei, um die Frage zu beantworten: Was innerhalb dieses Baums belegt den Platz? Der eine Befehl liefert eine Kennzahl pro Dateisystem, der andere eine Aufschlüsselung pro Verzeichnis.
Daraus ergibt sich in der Praxis ein fester Workflow: Zuerst df -h ausführen, um festzustellen, welche Partition kritisch voll ist, zum Beispiel /var oder /. Danach mit du gezielt in diese Partition hineingehen und Verzeichnis für Verzeichnis eingrenzen, bis der eigentliche Verursacher gefunden ist. Wer nur eines der beiden Werkzeuge kennt, verliert bei einem akuten Speicherplatzproblem wertvolle Zeit, weil entweder der Überblick oder die Detailtiefe fehlt.
2. df: Speicherplatz auf Dateisystem-Ebene messen
df -h liefert eine für Menschen lesbare Übersicht aller gemounteten Dateisystem mit Gesamtgröße, belegtem Platz, freiem Platz und Prozentangabe. Wichtig ist, gezielt nach der Zeile mit dem betroffenen Mountpoint zu suchen, etwa / oder /var/www, statt sich nur die erste Zeile anzusehen. Auf Servern mit separaten Partitionen für /var/log oder /tmp kann eine dieser kleineren Partitionen voll sein, während die Root-Partition noch reichlich Platz hat, das Symptom im Log ist dann oft ein „No space left on device“, obwohl df -h / harmlos aussieht.
Ein zweiter, oft übersehener Engpass sind Inodes statt Blöcken: df -ih zeigt die Auslastung der Inode-Tabelle. Wenn ein Verzeichnis Millionen kleiner Dateien enthält, zum Beispiel PHP-Session-Dateien oder Cache-Fragmente, kann die Inode-Tabelle voll laufen, obwohl auf dem Datenträger selbst noch Platz frei ist. In diesem Fall meldet das System ebenfalls „No space left on device“, aber df -h zeigt fälschlich freien Speicherplatz, nur df -ih deckt das eigentliche Problem auf. df -hT ergänzt zusätzlich den Dateisystemtyp, was bei gemischten Setups mit ext4, xfs und tmpfs-Mounts für Overlay-Container hilfreich ist.
# Human-readable overview of all mounted filesystems
df -h
# Example output
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 40G 38G 1.2G 97% /
# /dev/sdb1 20G 4.1G 15G 22% /var/log
# tmpfs 2.0G 0 2.0G 0% /dev/shm
# Check inode usage when "No space left" occurs despite free disk space
df -ih
# Example output
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/sda1 2.6M 2.6M 412 100% /
# Also show filesystem type (ext4, xfs, tmpfs, overlay)
df -hT /var/www
# Check a specific partition only, ideal for monitoring scripts
df -h --output=pcent,target /var | tail -n1
3. du: den Verursacher im Verzeichnisbaum finden
Sobald df die betroffene Partition identifiziert hat, übernimmt du die Detailarbeit. Der klassische Einstieg ist du -sh * | sort -rh | head -20 im vermuteten Wurzelverzeichnis, zum Beispiel /var/www oder /var. Das Flag -s summiert jeden Eintrag zu einer einzigen Zeile statt jede Unterdatei einzeln aufzulisten, -h formatiert die Größen menschenlesbar, und sort -rh sortiert absteigend nach Größe unter Berücksichtigung der Einheiten. So erscheint der größte Verzeichnis-Eintrag sofort ganz oben, ohne dass man die Ausgabe manuell durchsuchen muss.
Für eine schrittweise Eingrenzung eignet sich du -h --max-depth=1 /var | sort -rh, das nur eine Ebene tief geht und die Verzeichnisse einer Ebene direkt vergleichbar macht. Sobald der auffällige Unterordner gefunden ist, wiederholt man denselben Befehl eine Ebene tiefer, bis die konkrete Datei oder Dateigruppe sichtbar wird. Wichtig: du zählt standardmäßig die tatsächlich auf der Platte belegten Blöcke, nicht die logische Dateigröße, bei stark fragmentierten oder sparse Dateien kann das leicht abweichen. Das moderne Tool ncdu bietet dieselbe Information interaktiv mit Tastatursteuerung und ist auf produktiven Servern eine deutliche Zeitersparnis gegenüber wiederholten du-Aufrufen.
# Largest directories in the current path, sorted descending
du -sh * | sort -rh | head -20
# Example output
# 4.2G vendor
# 2.8G var
# 1.1G pub
# 340M app
# Step-by-step narrowing: only one level deep
du -h --max-depth=1 /var | sort -rh
# Example output
# 2.6G /var/log
# 1.8G /var/cache
# 210M /var/lib
# Check the size of individual log files, including the total
du -csh /var/log/*.log
# Interactive alternative for repeated analysis
ncdu /var/www
4. Wenn df voll zeigt, du aber nicht aufgeht
Ein klassisches und immer wieder verwirrendes Szenario: df -h meldet eine Partition zu 98 % voll, aber du -sh /* summiert am Ende deutlich weniger als die von df gemeldete Belegung. Die Ursache liegt fast immer an gelöschten, aber noch geöffneten Dateien. Löscht man eine Datei mit rm, während ein Prozess sie noch geöffnet hat, entfernt der Kernel lediglich den Verzeichniseintrag, der Speicherplatz auf der Platte wird erst freigegeben, wenn der letzte Prozess den Dateideskriptor schließt. du sieht die Datei nicht mehr, weil sie keinen Namen mehr im Dateisystem hat, df zählt sie aber weiterhin mit, weil die Blöcke physisch belegt bleiben.
Typischer Auslöser auf PHP-Servern: Ein PHP-FPM- oder Nginx-Prozess schreibt in eine Logdatei, ein Administrator löscht diese Logdatei direkt mit rm statt sie über logrotate zu rotieren, der laufende Prozess hält seinen Dateideskriptor aber weiter offen und schreibt munter weiter, ohne dass der Platz je wieder frei wird. Mit lsof +L1 lassen sich genau solche offenen Dateien mit einem Link-Count kleiner eins aufspüren, die Ausgabe zeigt Prozess-ID, Dateigröße und den ursprünglichen Pfad mit dem Zusatz (deleted). Die einzig saubere Lösung ist ein Neustart oder ein gezieltes Signal an den betroffenen Prozess, damit er die Datei neu öffnet, ein einfaches erneutes Löschen bewirkt nichts mehr.
# Confirm the discrepancy: df reports full, du counts less
df -h /
du -csh /* 2>/dev/null | tail -n1
# Find deleted files that are still held open
lsof +L1
# Example output
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
# php-fpm 4821 www-data 12w REG 8,1 3.4G 0 918273 /var/log/php-fpm/error.log (deleted)
# Alternative: filter specifically for "deleted"
lsof / 2>/dev/null | grep deleted
# Clean fix: release the file descriptor by reloading the service
systemctl reload php8.3-fpm
# Emergency only: truncate the file without restarting the process
# (careful, this discards any buffered log lines)
: > /proc/4821/fd/12
5. Log-Dateien: der häufigste Speicherfresser
Auf produktiven PHP-Servern sind Logdateien die mit Abstand häufigste Ursache für plötzlich volle Partitionen. Eine ungewöhnlich hohe Fehlerrate in der Anwendung, ein Bug, der bei jedem Request eine Exception wirft, oder ein Debug-Modus, der versehentlich in der Produktion aktiv bleibt, lässt error.log oder Magentos var/log/exception.log innerhalb weniger Stunden auf mehrere Gigabyte anwachsen. Ohne logrotate oder eine vergleichbare Rotation wächst diese Datei unbegrenzt weiter, bis die Partition voll ist und der komplette Server, inklusive Datenbank auf derselben Platte, blockiert.
Die Systemjournale sind ein zweiter, oft vergessener Verursacher. journalctl persistiert standardmäßig unter /var/log/journal und kann ohne Größenlimit ebenfalls beträchtlichen Platz belegen, insbesondere bei Diensten mit hoher Log-Frequenz wie Docker-Containern. journalctl --disk-usage zeigt die aktuelle Belegung, journalctl --vacuum-size=500M begrenzt sie sofort auf einen definierten Wert. Für Anwendungslogs gilt dieselbe Regel wie für Systemlogs: Rotation und Kompression gehören von Anfang an ins Deployment, nicht erst, wenn die Platte bereits voll ist.
# /etc/logrotate.d/magento
# Rotation for Magento and PHP-FPM logs on a production server
/var/www/html/var/log/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0644 www-data www-data
sharedscripts
postrotate
systemctl reload php8.3-fpm > /dev/null 2>&1 || true
endscript
}
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
systemctl reload nginx > /dev/null 2>&1 || true
endscript
}
6. Cache- und Session-Verzeichnisse aufräumen
Cache-Verzeichnisse gehören zu den Speicherfressern, die schleichend wachsen und daher selten sofort auffallen. In Magento sammeln sich unter var/cache, var/page_cache und var/view_preprocessed über Wochen hinweg zehntausende kleiner Dateien an, insbesondere wenn der Full Page Cache nicht über Redis oder Varnish, sondern auf dem lokalen Dateisystem läuft. du -sh var/cache var/page_cache var/view_preprocessed liefert schnell einen Überblick, wie viel jeder dieser Ordner tatsächlich belegt, häufig sind es mehrere Gigabyte reiner Cache-Ballast, der jederzeit gefahrlos gelöscht werden kann, weil er beim nächsten Request automatisch neu erzeugt wird.
PHP-Sessions sind der zweite klassische Fall: Der Standardpfad /var/lib/php/sessions sammelt bei fehlendem Session-Garbage-Collector über Monate hinweg abgelaufene Session-Dateien an, oft mit einer Inode-Anzahl im sechsstelligen Bereich, was eher die Inode-Tabelle als den Speicherplatz selbst belastet. find /var/lib/php/sessions -name "sess_*" -mmin +1440 -delete entfernt Sessions, die seit 24 Stunden nicht mehr angefasst wurden, deutlich zuverlässiger als sich auf die eingebaute PHP-Garbage-Collection zu verlassen, die bei hoher Session-Anzahl gerne stehen bleibt. Wer den eingesparten Platz nach dem Aufräumen prüfen will, vergleicht einfach du -sh var/cache var/page_cache var/view_preprocessed und find /var/lib/php/sessions -name "sess_*" | wc -l vor und nach dem Lauf.
7. Alte Releases, vendor-Verzeichnisse und Deployment-Artefakte
Wer mit einem symlink-basierten Deployment arbeitet, bei dem jedes Deployment ein neues Verzeichnis unter releases/ anlegt und der aktive Symlink lediglich umgebogen wird, häuft mit jedem Deploy einen kompletten neuen vendor-Ordner samt Composer-Abhängigkeiten und kompiliertem Frontend-Code an. Ohne eine Begrenzung der aufbewahrten Releases wächst dieses Verzeichnis linear mit der Anzahl der Deployments, ein einzelnes Magento-Release mit vollständigem vendor-Verzeichnis und generierten Static-Files kann leicht mehrere Gigabyte umfassen. Nach hundert Deployments sind das schnell mehrere hundert Gigabyte, die ausschließlich aus nicht mehr benötigten alten Ständen bestehen.
Die gängige Lösung ist eine feste Aufbewahrungsgrenze, üblicherweise die letzten drei bis fünf Releases, alles Ältere wird automatisch nach jedem erfolgreichen Deploy gelöscht. Deployment-Tools wie Deployer oder Capistrano bringen diese Logik bereits eingebaut mit, bei eigenen Skripten reicht ein einfacher find-Befehl, der Releases nach Änderungsdatum sortiert und alles außerhalb der gewünschten Anzahl entfernt. Der Befehl du -sh releases/* | sort -h zeigt vorab, wie groß jedes einzelne Release ist, und ls -1dt releases/*/ | tail -n +6 | xargs -r rm -rf entfernt alles außer den fünf neuesten. Wichtig ist, vor dem Löschen zu prüfen, dass keine der aufzuräumenden Verzeichnisse gerade das Ziel des aktiven Symlinks ist, sonst reißt man sich die laufende Produktion weg.
8. Automatisierte Aufräumroutinen und Monitoring
Manuelle Aufräumaktionen lösen ein akutes Problem, verhindern aber nicht, dass sich dasselbe Muster in vier Wochen wiederholt. Nachhaltig wird die Lösung erst durch Automatisierung: ein wöchentlicher Cron-Job oder systemd-Timer, der Logs rotiert, alte Releases entfernt und abgelaufene Sessions löscht, kombiniert mit einem Monitoring-Check, der frühzeitig warnt, bevor eine Partition kritisch wird. Ein einfacher Schwellenwert-Check über df --output=pcent in einem Monitoring-System wie Prometheus mit node_exporter oder einem simplen Nagios-Check reicht bereits, um bei 85 % Belegung eine Benachrichtigung auszulösen, lange bevor die Platte bei 100 % tatsächlich blockiert.
Für Infrastruktur, die über Ansible verwaltet wird, lässt sich dieselbe Aufräumlogik als wiederverwendbares Playbook definieren, das auf mehreren Servern konsistent läuft und über einen Cron-Trigger oder eine CI-Pipeline regelmäßig ausgeführt wird. Der entscheidende Vorteil gegenüber Ad-hoc-Skripten auf einzelnen Servern: Änderungen an der Aufräumlogik werden zentral versioniert und einheitlich auf die gesamte Flotte ausgerollt, statt dass jeder Server seine eigene, im Laufe der Zeit auseinanderdriftende Variante des Cleanup-Skripts pflegt.
# ansible/playbooks/disk-cleanup.yml
# Reusable playbook for regular disk space maintenance
---
- name: Disk space cleanup on PHP application servers
hosts: app_servers
become: true
vars:
releases_to_keep: 5
session_max_age_minutes: 1440
tasks:
- name: Check current disk usage before cleanup
command: df --output=pcent,target /var
register: disk_before
changed_when: false
- name: Remove old Magento releases beyond retention limit
shell: |
cd /var/www/releases
ls -1dt */ | tail -n +{{ releases_to_keep + 1 }} | xargs -r rm -rf
args:
executable: /bin/bash
- name: Clean up expired PHP sessions
find:
paths: /var/lib/php/sessions
patterns: "sess_*"
age: "{{ session_max_age_minutes }}m"
recurse: false
register: old_sessions
- name: Delete matched session files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_sessions.files }}"
- name: Alert if disk usage still above threshold
debug:
msg: "WARNING: disk usage still critical after cleanup"
when: disk_before.stdout | regex_search('([0-9]+)%') | int > 85
9. du und df im direkten Vergleich
Beide Werkzeuge messen Speicherplatz, aber auf grundlegend unterschiedlichen Ebenen und mit unterschiedlichen Datenquellen. Wer beide Ebenen kennt, spart bei jeder Speicherplatz-Analyse mehrere Umwege und findet den tatsächlichen Verursacher deutlich schneller.
| Frage | Falscher Ansatz | Richtiges Werkzeug | Vorteil |
|---|---|---|---|
| Ist die Partition voll? | du -sh / (langsam, rekursiv) |
df -h |
Liest Superblock direkt, sofortige Antwort |
| Welches Verzeichnis belegt den Platz? | df -h (nur pro Dateisystem) |
du -sh * | sort -rh |
Detailtiefe pro Verzeichnis |
| df voll, du zählt weniger | Erneut rm auf bereits gelöschte Datei |
lsof +L1 |
Findet offene, gelöschte Dateideskriptoren |
| Inode-Tabelle voll? | df -h allein (zeigt nur Blöcke) |
df -ih |
Zeigt Inode-Auslastung getrennt von Blockauslastung |
| Wiederholte Analyse auf Produktivserver | Mehrfach manuelles du pro Ebene |
ncdu |
Interaktiv, ein Durchlauf, sofortige Navigation |
In der Praxis ergänzen sich df und du in fast jeder Speicherplatz-Analyse: df liefert die Alarmmeldung, du liefert die Diagnose, und Werkzeuge wie lsof und ncdu schließen die Lücken, die keines der beiden Basiswerkzeuge allein abdeckt. Wer diese Kombination beherrscht, braucht für die allermeisten Speicherplatzprobleme auf einem PHP-Server keine zusätzliche Software.
Mironsoft
Server-Monitoring, Deployment-Automatisierung und Linux-Infrastruktur für Magento-Shops
Server, der zuverlässig nicht vollläuft?
Wir richten Log-Rotation, automatisiertes Release-Cleanup und Disk-Space-Monitoring für eure Magento- und PHP-Server ein, damit eine volle Partition nie wieder zum nächtlichen Notfall wird.
Disk-Space-Audit
Systematische df/du-Analyse und Priorisierung der größten Speicherfresser
Cleanup-Automatisierung
logrotate, Release-Retention und Session-Cleanup als versioniertes Playbook
Monitoring-Setup
Schwellenwert-Alerts für Speicherplatz und Inodes, bevor die Platte vollläuft
10. Zusammenfassung
Die Disk-Space-Analyse mit du und df folgt einem klaren Muster: df -h zeigt zuerst, welches Dateisystem kritisch voll ist, df -ih deckt zusätzlich volle Inode-Tabellen auf, die df -h allein übersieht. du -sh * | sort -rh grenzt anschließend Ebene für Ebene ein, bis der tatsächliche Verursacher sichtbar wird. Wenn df voll meldet, aber du nicht aufgeht, steckt fast immer eine gelöschte, aber noch offene Datei dahinter, aufgespürt mit lsof +L1 und behoben durch einen Neustart des betroffenen Prozesses.
Auf PHP-Servern sind die immer wiederkehrenden Verursacher fast identisch: unrotierte Logs, wachsende Cache- und Session-Verzeichnisse sowie Deployment-Systeme, die alte Releases nie löschen. Wer diese drei Bereiche mit logrotate, regelmäßigem Cache-Cleanup und einer festen Release-Retention automatisiert und zusätzlich einen Schwellenwert-Alert auf df legt, verhindert die meisten akuten Speicherplatzprobleme, bevor sie überhaupt zum nächtlichen Notfall werden.
Disk-Space-Analyse mit du und df, das Wichtigste auf einen Blick
df zuerst
df -h und df -ih zeigen, welches Dateisystem oder welche Inode-Tabelle kritisch belegt ist. Immer der erste Schritt.
du für die Details
du -sh * | sort -rh findet den Verursacher Ebene für Ebene, ncdu beschleunigt wiederholte Analysen.
Gelöschte, offene Dateien
df voll, du zählt weniger: lsof +L1 findet den offenen Dateideskriptor, ein Neustart gibt den Platz frei.
Automatisiertes Cleanup
logrotate, Release-Retention und Session-Cleanup als Cron-Job oder Ansible-Playbook, ergänzt um Schwellenwert-Monitoring.