Inodes und Dateisystem-Limits verstehen
$
/etc
Linux · Dateisysteme · Inodes · Server-Administration
Inodes und Dateisystem-Limits verstehen
Warum der Speicherplatz frei ist und der Server trotzdem voll läuft

Ein Server kann randvoll wirken, obwohl df -h noch reichlich freien Speicherplatz zeigt, weil nicht der Plattenplatz, sondern die Anzahl verfügbarer Inodes aufgebraucht ist. Dieser Artikel erklärt, was ein Inode speichert, wie man Inode-Exhaustion mit df -i sicher diagnostiziert und wie PHP-Anwendungen mit Millionen kleiner Cache- und Session-Dateien das Limit von Anfang an vermeiden.

12 Min. Lesezeit df -i · Inode-Exhaustion · mkfs · PHP-Sessions ext4 · XFS · Linux · Magento

1. Was ein Inode wirklich speichert

Jede Datei auf einem Linux-Dateisystem besteht aus zwei getrennten Bestandteilen: dem Dateinamen, der nur als Eintrag in einem Verzeichnis existiert, und dem Inode, der die eigentlichen Metadaten speichert. Ein Inode enthält Besitzer, Berechtigungen, Zeitstempel, Dateigröße und Zeiger auf die Datenblöcke, in denen der Inhalt liegt, aber explizit nicht den Dateinamen selbst. Deshalb kann eine Datei mehrere Hardlinks mit unterschiedlichen Namen haben, die alle auf denselben Inode zeigen, ohne dass der Inhalt dupliziert wird. Mit ls -i lässt sich die Inode-Nummer jeder Datei anzeigen, mit stat Datei bekommt man alle gespeicherten Metadaten im Detail.

Entscheidend für das Verständnis von Kapazitätsgrenzen ist, dass klassische Dateisysteme wie ext4 eine feste Anzahl an Inodes bereits beim Erstellen des Dateisystems reservieren, unabhängig vom tatsächlichen Speicherverbrauch. Diese Zahl wird über ein festes Verhältnis von Bytes pro Inode berechnet und ändert sich nachträglich nicht mehr, ohne das Dateisystem neu zu erstellen. Jede Datei, jedes Verzeichnis und jeder Symlink verbraucht dabei genau einen Inode, unabhängig davon, ob die Datei ein Byte oder zehn Gigabyte groß ist. Genau diese Eigenschaft führt in der Praxis dazu, dass ein Server, der überwiegend viele kleine Dateien erzeugt, seine Inodes lange vor seinem Speicherplatz aufbraucht.

2. df -i vs. df -h: Speicherplatz ist nicht Inode-Anzahl

Das Kommando df -h zeigt die Belegung des Speicherplatzes in Blöcken an, während df -i ausschließlich die Inode-Auslastung anzeigt, also wie viele der reservierten Inode-Slots bereits vergeben sind. Beide Werte laufen vollständig unabhängig voneinander, weil sie unterschiedliche Ressourcen des Dateisystems messen. Ein Server kann bei df -h eine Auslastung von vierzig Prozent melden und trotzdem bei df -i bereits bei hundert Prozent stehen, wenn überwiegend sehr kleine Dateien geschrieben wurden. Genau dieser Widerspruch ist das häufigste Anzeichen für eine bevorstehende oder bereits eingetretene Inode-Exhaustion.

In der Praxis lohnt es sich, beide Kommandos routinemäßig gemeinsam auszuführen, weil Monitoring-Systeme oft nur die Blockauslastung überwachen und Inode-Probleme dadurch unbemerkt bleiben, bis die Anwendung mit einer kryptischen Fehlermeldung stehen bleibt. Die Spalte IUse% in der Ausgabe von df -i entspricht dabei direkt der Spalte Use% von df -h, nur eben für Inodes statt Bytes. Wer Nagios, Zabbix oder den Prometheus Node Exporter einsetzt, sollte prüfen, ob Inode-Metriken bereits als eigener Alert-Kanal konfiguriert sind, statt sich allein auf die Speicherplatz-Auslastung zu verlassen.


# Compare block usage and inode usage on the same filesystem
$ df -h /var
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   19G   29G  40% /var

$ df -i /var
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      3276800 3276800      0  100% /var

# 40% block usage but 100% inode usage: classic inode exhaustion signature

3. Warum ein Dateisystem trotz freiem Speicherplatz voll läuft

Sobald die Inode-Tabelle eines Dateisystems vollständig belegt ist, verweigert der Kernel jede weitere Dateierstellung mit der Fehlermeldung No space left on device, obwohl noch reichlich freier Speicherplatz vorhanden ist. Für Anwendungsentwickler und Administratoren ist diese Meldung irreführend, weil sie exakt dieselbe ist, die auch bei tatsächlich vollem Speicherplatz erscheint. Ohne den gezielten Blick auf df -i bleibt die eigentliche Ursache oft stundenlang verborgen, während Deployments fehlschlagen, Sessions nicht mehr geschrieben werden können und Cron-Jobs mit Schreibfehlern abbrechen.

Besonders anfällig sind Systeme, die große Mengen sehr kleiner Dateien erzeugen: Mail-Server im Maildir-Format, Build-Systeme mit tausenden Zwischendateien, und vor allem PHP-Anwendungen mit dateibasiertem Session- und Cache-Handling. Ein einzelnes Verzeichnis mit mehreren Millionen leeren oder wenige Byte großen Dateien reicht aus, um ein Standard-ext4-Dateisystem mit typischer Inode-Ratio vollständig zu erschöpfen, obwohl der belegte Speicherplatz kaum ins Gewicht fällt. Die Ursache liegt fast immer in fehlender Bereinigung, nicht in echtem Datenwachstum.

4. Inode-Exhaustion diagnostizieren und lokalisieren

Der erste Schritt bei jeder Inode-Diagnose ist df -i, um festzustellen, welches Dateisystem betroffen ist und wie nah es an der Kapazitätsgrenze liegt. Danach muss ermittelt werden, welches Verzeichnis für den Großteil der Inode-Nutzung verantwortlich ist. Da es kein direktes du-Äquivalent für Inode-Zahlen gibt, behilft man sich mit find in Kombination mit wc -l oder mit spezialisierten Tools wie ncdu, das im Dateizähl-Modus gestartet werden kann.

Eine bewährte Methode ist, rekursiv durch die oberste Verzeichnisebene zu iterieren und pro Verzeichnis die Anzahl enthaltener Dateien zu zählen, um den Hauptverursacher einzugrenzen. Dieser Vorgang kann bei Millionen Dateien mehrere Minuten dauern, weil jede Zählung einen vollständigen Verzeichnis-Traversal erfordert, der bei ext4 nicht in konstanter Zeit läuft. Auf produktiven Systemen empfiehlt es sich, die Diagnose mit ionice und nice zu drosseln, um den laufenden Betrieb während der Analyse nicht zusätzlich zu belasten.


#!/usr/bin/env bash
# Find directories with the highest inode (file) count under /var
set -euo pipefail

for dir in /var/*/; do
  count=$(find "$dir" -xdev -type f 2>/dev/null | wc -l)
  printf "%10d  %s\n" "$count" "$dir"
done | sort -rn | head -n 10

# Drill deeper into the top offender, throttled to avoid I/O pressure
ionice -c3 nice -n19 find /var/www/html/var/session -xdev -type f | wc -l

5. Typische PHP-Ursachen: Sessions, Cache und Tmp-Dateien

PHP schreibt in der Standardkonfiguration jede Session als einzelne Datei nach session.save_path, meist /var/lib/php/sessions oder /tmp. Bei hohem Traffic ohne funktionierende Garbage Collection sammeln sich dort schnell Millionen verwaister Sessiondateien an, weil session.gc_probability und session.gc_divisor bei vielen Distributionen konservativ konfiguriert sind oder der Cronjob-basierte Cleanup gar nicht läuft. Jede dieser Dateien ist oft nur wenige hundert Byte groß, belastet also den Speicherplatz kaum, verbraucht aber jeweils einen vollen Inode.

Ähnlich verhält es sich mit dateibasiertem Opcode- und Objekt-Caching sowie mit Log-Rotationen, die neue Dateien statt Append-Operationen nutzen. Frameworks wie Symfony und Magento legen im Entwicklungsmodus oder bei falscher Konfiguration Zehntausende Cache-Dateien an, die ohne Rotation immer weiter anwachsen. Auch Composer- und npm-Installationen mit tief verschachtelten node_modules-Bäumen erzeugen überproportional viele kleine Dateien im Verhältnis zu ihrer Gesamtgröße und können bei CI-Systemen mit vielen parallelen Builds spürbar zur Inode-Auslastung beitragen.


; php.ini: aggressive session garbage collection to prevent inode exhaustion
[Session]
session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440

; Better: move sessions off the filesystem entirely
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=2"

6. Magento- und Hyvä-spezifische Inode-Fallen

In Magento-Shops sind var/cache, var/page_cache, var/session und var/log die klassischen Kandidaten für unkontrolliertes Datei-Wachstum. Insbesondere der dateibasierte Session-Handler erzeugt bei Shops mit hohem Gästetraffic und ohne Redis-Integration kontinuierlich neue Session-Dateien, die ohne aktive Garbage Collection nie gelöscht werden. Wird stattdessen Redis als Session- und Cache-Backend konfiguriert, entfällt dieser Inode-Verbrauch vollständig, weil Redis Schlüssel im Arbeitsspeicher statt als Einzeldateien im Dateisystem verwaltet.

Ein zweiter häufiger Fall ist der Full Page Cache im Dateisystem-Modus, der für jede gecachte Seitenvariante eine eigene Datei anlegt. Bei großen Katalogen mit vielen Filterkombinationen kann das in kurzer Zeit mehrere hunderttausend Dateien erzeugen. Die Umstellung auf Varnish oder Redis als Cache-Backend über app/etc/env.php löst dieses Problem strukturell, während bin/magento cache:flush allein nur den Inhalt leert, aber nicht verhindert, dass sich das Muster wiederholt.


{
  "cache": {
    "frontend": {
      "default": {
        "backend": "Cm_Cache_Backend_Redis",
        "backend_options": { "server": "127.0.0.1", "port": "6379", "database": "0" }
      },
      "page_cache": {
        "backend": "Cm_Cache_Backend_Redis",
        "backend_options": { "server": "127.0.0.1", "port": "6379", "database": "1" }
      }
    },
    "session": {
      "save": "redis",
      "redis": { "host": "127.0.0.1", "port": "6379", "database": "2" }
    }
  }
}

7. Inode-Anzahl beim Erstellen eines Dateisystems festlegen

Wer bereits weiß, dass ein Dateisystem viele kleine Dateien aufnehmen wird, sollte die Inode-Anzahl beim Erstellen gezielt planen, statt sich auf den Standardwert zu verlassen. Bei ext4 legt mkfs.ext4 die Inode-Anzahl über die Option -i fest, die das Verhältnis von Bytes pro Inode angibt, oder direkt über -N, das die absolute Anzahl an Inodes vorgibt. Der Standardwert liegt bei einem Inode pro 16 Kibibyte, was für Server mit vielen kleinen Dateien wie Session-Speichern oder Mail-Verzeichnissen deutlich zu wenig ist.

XFS geht einen anderen Weg und allokiert Inodes standardmäßig dynamisch nach Bedarf, wodurch feste Limits seltener zum Problem werden. Bei sehr vielen kleinen Dateien kann dennoch die Option maxpct begrenzend wirken, weshalb sie bei absehbar hoher Dateizahl bewusst erhöht werden sollte. Eine nachträgliche Änderung der Inode-Anzahl eines bestehenden ext4-Dateisystems ist ohne Neuerstellung nicht möglich, weshalb diese Entscheidung idealerweise schon bei der Server-Provisionierung getroffen wird. Mit tune2fs -l /dev/sdb1 | grep -i inode lässt sich die aktuelle Inode-Kapazität und der freie Bestand eines bestehenden Dateisystems jederzeit einsehen, bevor eine Migration ansteht.

8. Sofortmaßnahmen und langfristige Strategien

Im akuten Notfall, wenn ein Dateisystem bereits bei hundert Prozent Inode-Auslastung steht, hilft nur das gezielte Löschen von Dateien im betroffenen Verzeichnis, bis wieder freie Inodes verfügbar sind. Abgelaufene Sessiondateien lassen sich sicher anhand ihres Änderungsdatums identifizieren und mit find -mtime in Kombination mit -delete entfernen, ohne aktive Sessions zu gefährden. Vor jedem Massenlöschen empfiehlt sich ein Trockenlauf ohne -delete, um die betroffene Dateimenge zu verifizieren, bevor tatsächlich gelöscht wird.

Langfristig ist die nachhaltigste Lösung, Dateien, die naturgemäß kurzlebig und zahlreich sind, gar nicht erst auf dem persistenten Dateisystem abzulegen. Ein tmpfs-Mount für Session-Verzeichnisse hält diese Dateien im Arbeitsspeicher, wo Inode-Limits deutlich großzügiger und meist an die verfügbare RAM-Größe statt an eine feste Zahl gekoppelt sind. Ergänzend sorgt ein regelmäßiger Cronjob oder ein systemd-tmpfiles-Eintrag dafür, dass verwaiste Dateien nach einer definierten Lebensdauer automatisch entfernt werden, statt sich unbegrenzt anzusammeln.


# docker-compose.yml: move PHP session storage off the persistent filesystem
services:
  php-fpm:
    image: mironsoft/php:8.4-fpm
    volumes:
      - app_code:/var/www/html
    tmpfs:
      # Sessions live in RAM-backed tmpfs: no persistent inode consumption
      - /var/www/html/var/session:size=512m,uid=1000,gid=1000,mode=1777
    environment:
      PHP_SESSION_SAVE_PATH: /var/www/html/var/session

volumes:
  app_code:

9. Dateisystem-Limits im Vergleich

Je nach Dateisystem, Anwendungsfall und Konfiguration entscheidet sich, ob Inodes zum Engpass werden oder gar nicht erst auffallen. Die folgende Übersicht fasst die häufigsten Szenarien und die jeweils empfohlene Maßnahme zusammen.

Szenario Problem Empfohlene Maßnahme Effekt
ext4 mit Millionen Kleindateien Inode-Exhaustion trotz freiem Speicherplatz mkfs.ext4 -N bzw. kleinerer -i-Wert Ausreichend Inodes von Anfang an reserviert
PHP-Sessions ohne Redis Millionen verwaister Sessiondateien Redis-Session-Handler statt Dateisystem Inode-Verbrauch bleibt konstant bei null
Monitoring nur mit df -h Inode-Engpass bleibt unbemerkt df -i als eigener Alert-Kanal Fehlerursache in Sekunden erkannt
XFS ohne Kapazitätsplanung Dynamische Inodes ohne definiertes Limit maxpct bewusst setzen und überwachen Flexibilität ohne nachträgliches Resizing
Cache-Verzeichnis ohne Rotation Ungebremstes Datei-Wachstum bis zum Limit logrotate / tmpfiles.d mit Alter-Cleanup Planbare Obergrenze statt Überraschung

Auffällig ist, dass fast jede Zeile der Tabelle auf dasselbe Grundprinzip zurückführt: Inodes sind eine begrenzte, meist unsichtbare Ressource, die erst dann sichtbar wird, wenn sie bereits erschöpft ist. Wer df -i routinemäßig prüft und Anwendungen konsequent von unkontrolliertem Kleindatei-Wachstum entkoppelt, vermeidet den kompletten Fehlerkatalog aus dieser Übersicht.

Mironsoft

Server-Administration, Kapazitätsplanung und Magento-Infrastruktur

Inode-Probleme zuverlässig lösen und vermeiden?

Wir analysieren eure Server-Infrastruktur, identifizieren Inode-Fallen in PHP- und Magento-Anwendungen und richten Redis-Session-Handling, Log-Rotation und Dateisystem-Kapazitäten so ein, dass No space left on device der Vergangenheit angehört.

Inode-Audit

df -i-Monitoring, Diagnose der größten Verzeichnisse und Frühwarnung

Redis-Migration

Session- und Cache-Handling von Dateien auf Redis umstellen

Server-Provisionierung

Dateisysteme mit passender Inode-Kapazität von Anfang an planen

10. Zusammenfassung

Die wichtigste Erkenntnis rund um Inodes und Dateisystem-Limits: Speicherplatz und Inode-Anzahl sind zwei vollständig getrennte Ressourcen, die getrennt überwacht werden müssen. df -h zeigt niemals eine Inode-Erschöpfung an, nur df -i liefert die relevante Kennzahl. Millionen kleiner Dateien, wie sie PHP-Sessions, dateibasierte Caches und unrotierte Logs erzeugen, können ein Dateisystem lange vor dem eigentlichen Speicherplatz an sein Limit bringen. Die Fehlermeldung No space left on device ist dabei identisch, egal ob Blöcke oder Inodes ausgehen, was die Diagnose ohne gezielten Check erschwert.

Wirksame Gegenmaßnahmen setzen an drei Stellen an: bewusste Inode-Planung bei der Dateisystem-Erstellung mit mkfs -N oder -i, konsequentes Auslagern kurzlebiger Kleindateien auf tmpfs oder Redis statt auf das persistente Dateisystem, und automatisierte Bereinigung über logrotate oder systemd-tmpfiles. Wer diese drei Maßnahmen kombiniert und df -i fest ins Monitoring aufnimmt, verhindert, dass ein scheinbar leerer Server plötzlich keine neuen Dateien mehr annimmt.

Inodes und Dateisystem-Limits, das Wichtigste auf einen Blick

Inode vs. Speicherplatz

Jede Datei verbraucht genau einen Inode, unabhängig von ihrer Größe. Millionen Kleindateien erschöpfen Inodes lange vor dem Plattenplatz.

df -i als Pflicht-Check

df -h zeigt keine Inode-Auslastung. Nur df -i deckt Inode-Exhaustion auf, bevor sie zum Ausfall führt.

PHP-Sessions & Cache

Redis statt dateibasiertem Session- und Cache-Handling entfernt den größten Inode-Verbraucher vollständig.

Planung & Bereinigung

mkfs -N für ausreichend Inodes, tmpfs für kurzlebige Dateien, logrotate für automatische Bereinigung.

11. FAQ: Inodes und Dateisystem-Limits

1Was ist ein Inode genau?
Eine Datenstruktur mit allen Metadaten einer Datei: Besitzer, Berechtigungen, Zeitstempel, Größe und Zeiger auf Datenblöcke. Der Dateiname steht separat im Verzeichniseintrag.
2Warum kann ein Dateisystem voll sein, obwohl df -h noch Platz zeigt?
Speicherplatz und Inode-Anzahl sind getrennte Ressourcen. Viele kleine Dateien können die feste Inode-Anzahl aufbrauchen, während noch Gigabyte an Speicherplatz frei sind.
3Wie prüfe ich die Inode-Auslastung eines Servers?
Mit df -i. Zeigt belegte und freie Inodes sowie IUse% pro Dateisystem, das direkte Pendant zu df -h für Speicherplatz.
4Welche Dateien fressen typischerweise Millionen Inodes bei PHP-Anwendungen?
Dateibasierte PHP-Sessions ohne Garbage Collection, unrotierte Framework-Caches und tief verschachtelte node_modules-Verzeichnisse.
5Kann ich die Inode-Anzahl nachträglich erhöhen?
Bei ext4 nein, nur durch Neuerstellung mit mkfs. XFS allokiert dynamischer, ist aber ebenfalls nicht beliebig nachträglich erweiterbar.
6Wie wähle ich die richtige Inode-Anzahl beim Erstellen?
Mit mkfs.ext4 -i für das Bytes-pro-Inode-Verhältnis oder -N für die absolute Anzahl. Bei vielen Kleindateien ein deutlich dichteres Verhältnis als den Standard wählen.
7Ist tmpfs eine Lösung für Inode-Probleme?
Ja, für kurzlebige Dateien wie Sessions. tmpfs liegt im RAM und ist nicht an eine feste, vorreservierte Inode-Zahl gebunden.
8Wie finde ich das Verzeichnis mit dem höchsten Inode-Verbrauch?
Mit einer find-Schleife pro Verzeichnisebene oder mit ncdu im Dateizähl-Modus, bei Millionen Dateien gedrosselt mit ionice und nice.
9Was passiert, wenn Inodes ausgehen, aber Speicherplatz frei ist?
Der Kernel verweigert neue Dateien mit No space left on device. Deployments, Sessions und Cron-Jobs schlagen fehl, bis Inodes freigegeben werden.
10Hilft XFS oder Btrfs gegen Inode-Exhaustion?
Beide allokieren dynamischer als ext4 und sind seltener betroffen, aber Limits wie maxpct existieren weiterhin und unkontrolliertes Wachstum bleibt ein Risiko.