Troubleshooting

Fix "No space left on device" when the disk isn't full (inodes)

Linux says no space left on device but df -h shows free space? Use df -i to confirm inode exhaustion, find the folder with millions of files and clean it up safely.

IntermediateLinux server7 min read

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 find across / can take minutes on a busy server.
  • The commands used here — df, du, find, ls, lsof — ship with coreutils or are one install away. tune2fs comes 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.

Bash
df -h
df -i

Look 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:

Bash
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.

Bash
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.

Bash
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 -20

Keep 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:

Bash
sudo find /var -xdev -type f | wc -l

Identify what the files are

Look before you delete

Sample the directory and count files by age.

Bash
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 | tail

Common culprits

  • Mail queue. Thousands of small files in /var/spool/postfix/maildrop mean 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/sessions fills 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. /tmp and application upload folders that never clean themselves.
  • Docker. /var/lib/docker/overlay2 gains 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.

Bash
sudo find /var/lib/php/sessions -type f -mtime +1 -delete

For a queue that only contains cron noise:

Bash
sudo postqueue -p | head
sudo postsuper -d ALL

For /tmp, let systemd handle the directories it is configured for:

Bash
sudo systemd-tmpfiles --clean

For 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:

Bash
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.

Bash
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:

Bash
systemctl status postfix
journalctl -n 50 --no-pager

If 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 -i shows free inodes but writes still fail. The problem is blocks or the root-reserved 5%. Check df -h and sudo tune2fs -l /dev/vda1 | grep -i 'reserved block'.
  • du: unrecognized option '--inodes'. Your coreutils predates 8.22. Count per directory with sudo find /path -xdev -type f | wc -l instead.
  • The count refills within minutes. Something is still writing. Watch it with watch -n 5 'df -i /' and read journalctl -f plus the cron logs for a job that runs every minute.
  • find reports "Permission denied" everywhere. Run it with sudo, 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 +L1 and restart that service.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.