One of three ways a disk is full while df says it isn't
The other two: a deleted file that a running process still holds open, so its blocks are never freed (lsof +L1 lists those), and the systemd journal growing to its own cap (see journalctl Disk Usage). When the disk really is full of data, start with Find Large Files on Linux. This page is the inode case.
Every write on the box fails with No space left on device. You run df -h and the filesystem is at 0%, or 40%, or anything but full. Nothing is wrong with df -h: it counts data blocks, and you have plenty. What ran out is inodes, the per-file records that ext4 allocates once, at mkfs time, and never adds to. A directory that quietly collected a few million tiny files can use all of them while using almost no space, and the kernel reports that with the same error as a full disk.
What Does Inode Exhaustion Look Like?
Reproduced here on 2026-09-28 without root: a 50 MB tmpfs with its inode count capped at 1,000, mounted inside a user namespace (unshare -rm, then mount -t tmpfs -o size=50M,nr_inodes=1000 tmpfs /mnt). An app writes 40 log files, then a session directory fills with empty files until the kernel refuses:
Every other write on that filesystem now fails the same way, including one to a different directory:
The two df views of the same filesystem at the same moment (from the first run of this test):
0% of blocks used, 100% of inodes used. The files are empty, so they cost no data blocks at all, and each one still costs an inode. On a real server the numbers are bigger (this box's root filesystem has 57,843,712 inodes) and the mechanism is identical.
The Script
Save as inode-usage-check.sh. It prints block and inode use side by side, and when inode use crosses the threshold it names the directories holding the most entries on that filesystem.
What Does the Script Print?
On the full test filesystem:
The culprit is on the first line: 956 entries in sessions. Clear it with find, not rm sessions/*, because at real-world scale the glob expands past the kernel's argument limit (see Argument List Too Long):
The inodes come back the moment the files are unlinked; no remount or restart. On this box's real root filesystem, where the disk is nearly full by blocks and nowhere near full by inodes:
That is the healthy shape: block use and inode use moving independently. A filesystem where inode use is far ahead of block use is one where something is creating small files faster than anything removes them.
What Usually Eats the Inodes?
- Session stores. PHP's default
session.save_pathwrites one file per visitor session; if the garbage collector never runs (it is disabled on Debian and Ubuntu in favour of a cron job or systemd timer), they accumulate for years. - Mail and print queues. A dead relay leaves every outgoing message in
/var/spool/postfix/deferredas a file. - Caches. Package-manager caches, thumbnail caches, and application caches that write one file per key.
- Per-run temp files. A cron job that runs every minute and leaves one
mktempfile behind uses 525,600 inodes a year. That is what the trap cleanup pattern is for.
How Do I Schedule It?
Hourly, with cron mailing or logging any run that exits 1:
One line per filesystem you care about; /var and /tmp are the usual suspects when they are separate mounts. The disk space warning script covers the block side of the same alert. Both are the kind of unattended check that wants a lock, a log line and one alert per incident rather than one per hour, which is what The Production Bash Toolkit packages as bashlib.sh.
Frequently Asked Questions
Why does df -h show free space when I get No space left on device?
df -h reports data blocks, and your filesystem still has them. What it has run out of is inodes: one per file, directory or symlink, with the total fixed when an ext4 filesystem is created. Run df -i. If IUse% is 100%, no new file can be created anywhere on that filesystem, however much block space is free.
How do I find which directory is using all the inodes?
Count entries per parent directory, staying on the one filesystem: find /mount -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head. The top line is almost always one directory holding hundreds of thousands of small files: a session store, a mail or print queue, a cache, or a job that writes a temp file per run and never deletes it.
Can I add more inodes to an ext4 filesystem?
Not in place. ext4 sets the inode count at mkfs time from the bytes-per-inode ratio (-i) or an explicit count (-N). Growing the filesystem with resize2fs adds inodes in proportion to the added space, but the only way to change the ratio is to back up, recreate the filesystem with mkfs.ext4 -i 4096 or -N, and restore. XFS and btrfs allocate inodes dynamically and rarely hit this.
Deleting files didn't free inodes. Why?
An inode is released only when its last link is removed and no process holds the file open. If a running process still has deleted files open, the inodes stay in use until it closes them or exits. lsof +L1 lists open files with zero links; restart the process that holds them.
Does this happen on XFS or btrfs?
Rarely. XFS allocates inodes dynamically up to a percentage of the filesystem (imaxpct), and btrfs has no fixed inode table, so df -i on btrfs reports 0 or a dash. The script on this page detects the dynamic case and exits 0. Inode exhaustion is overwhelmingly an ext2/3/4 problem.
Part of the bash snippets collection
Related Scripts
- Disk Space Warning — the block-usage alert this one complements
- Find Large Files on Linux — when blocks, not inodes, are the problem
- journalctl Disk Usage and Vacuum — the systemd journal's share of a full disk
- Argument List Too Long — deleting the files once you have found them