Your server refuses to write files and reports No space left on device. You run df -h and the disk looks half empty. In many cases the filesystem has run out of inodes, not blocks. Every file, directory and symlink uses one inode, and a filesystem is created with a fixed number of them. This guide shows you how to confirm that, find the directory holding millions of tiny files, and clean it up without deleting anything you need.
Before you start
- SSH access to the VPS as root, or as a user with
sudo. If SSH itself is failing because the server cannot write, use the browser console in your VPS provider's control panel. - Comfort with the shell. A
findacross/can take minutes on a busy server. - The commands used here —
df,du,find,ls,lsof— ship with coreutils or are one install away.tune2fscomes from e2fsprogs.
Warning: Read each step fully before deleting anything. Emptying the wrong directory — a live mail queue, a database data directory, a webroot — can destroy data or stop services. When you are unsure, move files aside instead of deleting them.
Confirm it is inodes, not blocks
Check both views
Run both commands. -h shows blocks, -i shows inodes.
df -h
df -iLook at the IUse% column for the filesystem that reported the error. A classic failure looks like this:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 2621440 2621440 0 100% /IUse% at 100% while df -h still shows free space is your confirmation. Blocks are fine; there are no inodes left to create anything new.
Check reserved blocks too
On ext2/ext3/ext4, 5% of the blocks are reserved for root by default. That is why an unprivileged user can get No space left on device while df -h shows a few percent free. Check the real numbers:
sudo tune2fs -l /dev/vda1 | grep -i 'reserved block'Tip: If a disk is genuinely full of data and you only need a little headroom, lowering the reserved percentage with tune2fs -m 1 hands that space back. The full routine is in Free up disk space on a Linux VPS. Do not set it to 0 on the root filesystem — root needs room to recover a full disk.
Find the directory with millions of files
Start at the top
GNU du can count inodes instead of bytes. This one-liner lists the top-level directories on the root filesystem, sorted by inode count.
sudo du --inodes -x -d1 / 2>/dev/null | sort -n | tail -20-x keeps the walk on one filesystem and -d1 limits it to one level of depth.
Drill down
Take the largest result and repeat the command against it.
sudo du --inodes -x -d1 /var 2>/dev/null | sort -n | tail -20
sudo du --inodes -x -d1 /var/spool 2>/dev/null | sort -n | tail -20Keep going until the counts collapse into a single folder. You usually land somewhere like /var/spool/postfix/maildrop, /var/lib/php/sessions, /tmp, /var/log, a Docker overlay directory, or a home directory full of tiny files.
If du --inodes is missing on an older system, count files per directory instead:
sudo find /var -xdev -type f | wc -lIdentify what the files are
Look before you delete
Sample the directory and count files by age.
ls -la /var/spool/postfix/maildrop | head -20
sudo find /var/spool/postfix/maildrop -type f | wc -l
sudo find /var/spool/postfix/maildrop -type f -printf '%TY-%Tm-%Td\n' | sort | uniq -c | tailCommon culprits
- Mail queue. Thousands of small files in
/var/spool/postfix/maildropmean outbound mail is failing: a missing mail name, no relay, or a broken cron job that mails an error every minute. - PHP sessions.
/var/lib/php/sessionsfills when garbage collection is not running. - Cron output. A script that fails and mails a report every minute creates 1,440 files a day, plus one inbox file per message.
- Temporary uploads.
/tmpand application upload folders that never clean themselves. - Docker.
/var/lib/docker/overlay2gains inodes quickly when images and containers are created and removed over and over. - One-file-per-request logging. Some applications write a separate file for every request.
Clean up safely
Stop the source first
Deleting files while the process keeps writing them only refills the filesystem. Fix the cause before you clear the symptom: repair the mail relay, fix the noisy cron job, configure session garbage collection.
Warning: For a mail queue, look at what is queued before you flush it. postqueue -p | head shows the messages. If they are real customer emails, deliver them rather than delete them.
Remove aged files
Delete files older than a day, one directory at a time.
sudo find /var/lib/php/sessions -type f -mtime +1 -deleteFor a queue that only contains cron noise:
sudo postqueue -p | head
sudo postsuper -d ALLFor /tmp, let systemd handle the directories it is configured for:
sudo systemd-tmpfiles --cleanFor Docker leftovers, use Docker's own cleanup rather than deleting overlay directories by hand — see Free disk space with docker system prune.
Keep a way back
Note IUsed from df -i before you start, then check it again afterwards:
df -i /Tip: If you are not certain a directory is safe to empty, move it aside with mv /var/spool/postfix/maildrop /var/spool/postfix/maildrop.old, let the service recreate it, and restore the contents later if you need them.
When cleaning is not enough
ext4 and XFS cannot grow their inode count after the filesystem is created. If your workload genuinely needs millions of files — a mail server, a sync client directory, a build machine full of node_modules — the answer is a larger filesystem or a second disk, not more deleting. Reinstalling from your VPS provider's control panel onto a bigger plan gives you more inodes from the start.
Check it works
Confirm the counters moved and that the server can write again.
df -i
df -h
touch /tmp/write-test && rm /tmp/write-test && echo "writes OK"IUse% should now sit well below 100%, and any service that was failing should recover. Check the service itself:
systemctl status postfix
journalctl -n 50 --no-pagerIf the number climbs straight back up, the source is still running. Go back to "Identify what the files are" and deal with it there.
Troubleshooting
df -ishows free inodes but writes still fail. The problem is blocks or the root-reserved 5%. Checkdf -handsudo tune2fs -l /dev/vda1 | grep -i 'reserved block'.du: unrecognized option '--inodes'. Your coreutils predates 8.22. Count per directory withsudo find /path -xdev -type f | wc -linstead.- The count refills within minutes. Something is still writing. Watch it with
watch -n 5 'df -i /'and readjournalctl -fplus the cron logs for a job that runs every minute. findreports "Permission denied" everywhere. Run it withsudo, otherwise the counts are wrong and you will chase the wrong directory.- You deleted files but nothing was freed. A process still holds the deleted file open. Find it with
sudo lsof +L1and restart that service.
Next steps
- Free up disk space on a Linux VPS for the block-level version of this routine.
- Free disk space with docker system prune when containers are the cause.
- Monitor CPU, memory, disk and logs on a Linux server so you are warned before the next 100%.
- Move Docker data root to another disk or path if
/var/lib/dockerkeeps filling up.
