"No space left on device" but df shows free space
The block usage is fine but the filesystem has run out of inodes, or a deleted file is still held open by a process and its space has not been released.
What you see
Writes fail with ENOSPC. df -h shows plenty of free space on the filesystem in question, so the error looks nonsensical.
What is actually wrong
Either inode exhaustion — millions of tiny files, typically in a mail spool, a session directory or a cache — or a process holding a deleted file open, which keeps the blocks allocated until it closes or exits.
Codes and articles
Fixes (2)
Find and clear the inode consumer
df -i shows the inode table full. Deleting files is not reversible — check what they are before removing them.
Confirm which filesystem is out of inodes.
df -iFind the directories holding the most files. This walks the tree, so run it on the affected mount only.
sudo du --inodes -x -d 3 /var 2>/dev/null | sort -rn | head -20-x keeps it on one filesystem so it does not wander into /proc or a network mount and take all day.
On older systems without du --inodes, count directly.
sudo find /var -xdev -type f -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20Look at what the files actually are before deleting anything.
sudo ls -la /var/spool/postfix/maildrop | head -20Delete safely in batches — a plain rm * will fail with 'argument list too long' at this scale.
sudo find /var/spool/postfix/maildrop -type f -mtime +7 -deletefind -delete streams the deletions instead of building one enormous argument list, and the -mtime guard stops it taking files still in use.
Fix the cause: rotate the logs, set a cron job to clear the cache, or fix the application filling the directory.
df -i && touch /var/tmp/write-test && rm /var/tmp/write-test && echo OKRelease space held by a deleted-but-open file
Inodes are fine but space is not being returned. Classic after someone deletes a huge log that a running daemon still has open.
List deleted files still held open, largest first.
sudo lsof -nP +L1 | sort -k7 -rn | head -20+L1 selects files with a link count below 1 — that is exactly the set that has been unlinked but not closed.
Note the PID and the size. The space comes back when that file descriptor closes.
Preferred: reload the service so it reopens its logs.
sudo systemctl reload nginxIf reload is not supported, restart it.
sudo systemctl restart nginxAs a last resort — and only if you cannot restart the service — truncate the descriptor in place. Replace PID and FD with the values from lsof.
sudo truncate -s 0 /proc/12345/fd/4This frees the blocks without stopping the process. It discards the file contents, so do not do it to something you still need to read.
Set up logrotate with copytruncate for whatever produced the file, so it does not recur.
df -hRelated faults
Where this stops. This write-up was written and checked by hand. It says what each step changes, how to confirm it worked and how to reverse it, and anything destructive is flagged before you reach it. If it does not match what your machine is doing, search the Support Centre for the exact code or message — and when something needs a person, get in touch.