Linux Error Guide: 'No space left on device' ENOSPC Disk and Inode Exhaustion
Fix the Linux 'No space left on device' (ENOSPC) error: diagnose full filesystems, inode exhaustion, deleted-but-open files, reserved blocks, and runaway logs.
- #linux
- #troubleshooting
- #errors
- #disk
Stuck on this Linux Admins error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Guided workflow
Linux: no space left on device
The filesystem refuses writes. Whether df agrees with you decides which of three very different problems you have.
- Diagnose
- Inspect
- Interpret
- Remediate
- Validate
- Prevent
0 of 6 steps done
-
Two separate resources produce the same error message. Checking only one is why this problem sometimes appears unsolvable.
df -hRead-onlyExpect: Use% below 100 on the affected mount.
What it means: Identifies which filesystem is full — often not the one you assumed, and frequently /var rather than /.
df -iRead-onlyExpect: IUse% well below 100.
What it means: Inode exhaustion produces "no space left on device" on a filesystem with free space. Common wherever many small files accumulate — caches, session files, mail queues.
-
If df says full, find the consumers. If df and du disagree, the space is held by deleted-but-open files and no amount of searching for large files will find it.
du -xh --max-depth=1 /var 2>/dev/null | sort -rh | head -15Read-onlyWhat it means: `-x` stays on one filesystem, which stops you counting other mounts. Descend into the largest directory and repeat.
lsof +L1 2>/dev/null | head -20Read-onlyWhat it means: Files deleted while a process still holds them open. Their space is not returned until the descriptor closes — this is the exact case where df and du disagree.
journalctl --disk-usageRead-onlyWhat it means: The systemd journal is a very common culprit on servers that have been running a while without a size cap configured.
-
Three causes, three fixes.
- If: df -i shows inodes near 100%
- Millions of small files. Find the directory and remove them by age. Adding disk does not help; the inode count is fixed at filesystem creation.
- If: df says full but du finds much less
- Deleted-but-open files. Restart the process holding them — usually a log writer that was rotated without being signalled.
- If: df and du agree
- Genuine consumption. Remove or relocate, then set a retention policy so it does not simply refill.
-
Prefer signalling a process over deleting files it is using — deleting an open log frees nothing and loses the log.
journalctl --vacuum-size=500MChanges stateWhat it means: Trims the journal to a bounded size. Discards old logs, so confirm you do not need them for an ongoing investigation first.
systemctl restart <unit-holding-deleted-files>Changes stateWhat it means: Closes the descriptors and returns the space immediately. Usually the correct fix rather than deleting anything.
find /var/log -type f -name "*.gz" -mtime +30 -deleteDestructiveThis can destroy data or state. Read the interpretation below before running it.
What it means: Deletes rotated logs older than 30 days. Run without `-delete` first and read the list — a `find -delete` with a wrong path is unrecoverable.
-
Free space in df is not proof the problem is fixed. The failing process may write to a different mount than the one you cleared, may be blocked by a quota rather than by the filesystem, or may lack permission on a directory that is now empty. Test the actual write, as the actual user.
df -h && df -iRead-onlyExpect: Both space and inode usage comfortably below 100% on the affected mount.
What it means: Check both again — clearing files frees inodes and space at different rates, and a filesystem can come back under the space threshold while still being out of inodes.
sudo -u <service-user> touch /path/the/service/writes/.probe && rm /path/the/service/writes/.probeRead-onlyWhat it means: Tests the write as the actual user, on the actual path. Catches permission and quota problems that df cannot see.
-
Set `SystemMaxUse` in journald.conf. Configure logrotate with a signal to the writer, not just a rename. Alert at 80% on both space and inodes — the second is the one nobody monitors and the one that produces the confusing version of this incident. On CI runners, schedule a pruning job with a time filter, since they fill predictably.
Saves to your Toolkit when signed in, and to this browser otherwise.
Overview
No space left on device is the ENOSPC errno (28). The kernel returns it whenever a write cannot be completed because the target filesystem has no room. The confusing part is that “room” means two separate resources: free data blocks (raw bytes) and free inodes (the metadata slots that track each file). A filesystem can show plenty of free gigabytes and still fail every write because it has run out of inodes — or be 100% full of blocks while inodes are abundant. Either condition surfaces with the identical message.
You will see it from almost any program that writes:
write error: No space left on device
cp: error writing '/var/log/app/out.log': No space left on device
-bash: cannot create temp file for here-document: No space left on device
OSError: [Errno 28] No space left on device
It occurs on any write path: creating or extending a file, tar/cp/rsync, package installs, database commits, and even shell here-documents. The failure is per-filesystem, so a full /var can break logging while /home has terabytes free.
Symptoms
- Writes, package installs, and log rotation fail with “No space left on device”.
- Services crash or refuse to start because they cannot create lock/pid/temp files.
df -hmay show 100% use on the affected mount — or it may not (inode or deleted-file cases).- New files cannot be created even though
df -hreports free space.
df -h /var
df -i /var
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 20G 20G 0 100% /var
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 1310720 487213 823507 38% /var
Here blocks are 100% full while inodes are fine — a classic byte-exhaustion case. Always check both df -h and df -i.
Common Root Causes
1. The filesystem is genuinely full (data blocks exhausted)
The simplest case: the mount holding the write target has no free bytes. Identify which filesystem is full and confirm Avail is 0 / Use% is 100%.
df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 12G 36G 25% /
/dev/sda3 20G 20G 0 100% /var
/dev/sdb1 100G 18G 77G 19% /home
/var is full; anything writing under /var (logs, spool, databases) gets ENOSPC.
2. Inode exhaustion (free space but no inodes)
Each file/dir/symlink consumes one inode. Millions of tiny files (session caches, mail queues, PHP sessions) can exhaust inodes while leaving most of the disk empty. df -h looks healthy; df -i tells the truth.
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 198114 3078686 7% /
/dev/sda3 1310720 1310720 0 100% /var
IUse% is 100% on /var while df -h would show free gigabytes. To find the offending directory:
sudo find /var -xdev -type f | cut -d/ -f1-4 | sort | uniq -c | sort -rn | head
988231 /var/spool/postfix
142880 /var/lib/php/sessions
38104 /var/cache/nginx
3. A few very large files
One or two oversized files (an unrotated log, a core dump, a stray VM image) can fill the disk. Walk the tree by size from the mount point down.
sudo du -xh / --max-depth=1 2>/dev/null | sort -rh | head
sudo find /var -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
14G /var/log
3.1G /var/lib
512M /var/cache
-rw-r--r-- 1 syslog adm 13G Jun 23 09:41 /var/log/syslog
A single 13G syslog is eating the partition.
4. Deleted-but-open files held by a process
A process can hold an open file descriptor to a file that was rm-ed. The directory entry is gone (so du cannot see it) but the blocks are not freed until the process closes the fd or exits. df shows full; du shows much less.
sudo lsof +L1
sudo lsof -nP | grep '(deleted)' | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 2417 tomcat 37w REG 8,3 9663676416 0 131074 /var/log/app/catalina.out (deleted)
The deleted catalina.out still holds ~9G. Truncating the fd or restarting the process reclaims it:
sudo truncate -s 0 /proc/2417/fd/37
5. Reserved blocks for root (non-root writes fail first)
ext2/3/4 reserve a percentage of blocks (default 5%) for root. Once a filesystem hits that reserve, non-root processes get ENOSPC even though df shows a sliver of Avail usable only by root.
sudo tune2fs -l /dev/sda3 | grep -Ei 'Reserved block count|Block count'
Block count: 5242880
Reserved block count: 262144
262144 of 5242880 blocks (5%) are root-only. On a large data volume that is wasted space; lower it with tune2fs -m 1 /dev/sda3 (do not do this on /).
6. Runaway journald / application logs
systemd-journald or an app log can grow until it fills /var. Check journal disk usage and per-directory log size.
journalctl --disk-usage
sudo du -xh /var/log --max-depth=1 | sort -rh | head
Archived and active journals take up 8.0G in the file system.
8.1G /var/log/journal
2.4G /var/log/nginx
Vacuum the journal to reclaim space immediately:
sudo journalctl --vacuum-size=500M
Diagnostic Workflow
Step 1: Identify which filesystem is out of space
df -h
df -i
Compare both. Use% = 100% in df -h is block exhaustion; IUse% = 100% in df -i is inode exhaustion. Note the exact mount point.
Step 2: If blocks are full, find the largest consumers
sudo du -xh <MOUNT> --max-depth=1 2>/dev/null | sort -rh | head
sudo find <MOUNT> -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
-xdev keeps du/find on the one filesystem so you do not chase other mounts.
Step 3: Reconcile df vs du to catch deleted-open files
sudo lsof +L1
If df reports far more used than du accounts for, a process is holding deleted files open. Truncate the fd or restart the process.
Step 4: If inodes are exhausted, find the file-count hotspot
sudo find <MOUNT> -xdev -type f | cut -d/ -f1-5 | sort | uniq -c | sort -rn | head
This counts files per subtree to pinpoint the directory creating millions of small files.
Step 5: Reclaim and verify
sudo journalctl --vacuum-size=500M
sudo truncate -s 0 /var/log/<HUGE_LOG> # keeps the open fd valid
df -h <MOUNT> && df -i <MOUNT>
Prefer truncate -s 0 over rm for a log a service still has open, so you do not create a deleted-but-held file.
Example Root Cause Analysis
A web server starts throwing OSError: [Errno 28] No space left on device and nginx 5xx errors. df -h /var shows 100% used, so it looks like a plain full disk. But du -xh /var --max-depth=1 only accounts for about 6G on a 20G partition — the numbers do not add up.
The mismatch between df (full) and du (only 6G) points at deleted-but-open files:
sudo lsof +L1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
python 3391 appuser 14w REG 8,3 13958643712 0 262145 /var/log/app/debug.log (deleted)
A deploy script had rm-ed debug.log to “clear” it, but the running app still held the fd, so ~13G of blocks were never released. du could not see the file because its directory entry was gone, while df still counted the blocks.
Fix: truncate the live descriptor to release the blocks without restarting the app, then fix the rotation to truncate instead of delete:
sudo truncate -s 0 /proc/3391/fd/14
df -h /var
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 20G 6.2G 13G 34% /var
Space is reclaimed instantly and nginx recovers. The lasting fix is a logrotate copytruncate policy so log clearing never orphans an open fd again.
Prevention Best Practices
- Monitor both block and inode usage (
df -handdf -i) and alert before 90% on either — inode exhaustion is the one teams forget. - Configure logrotate with
copytruncate(or signal the daemon to reopen) so rotating a log never leaves a deleted-but-open file holding the disk. - Cap journald with
SystemMaxUse=injournald.confinstead of relying on manual vacuuming. - Put
/var,/tmp,/home, and database data on separate filesystems so one runaway writer cannot take down the whole host. - Clean ephemeral file factories (PHP sessions, mail spool, build caches) on a schedule before they exhaust inodes.
- Lower the ext4 reserved-block percentage on large non-root data volumes (
tune2fs -m 1) to stop wasting 5% — never touch/. - For broader disk and filesystem playbooks, see the Linux admins guides.
Quick Command Reference
# Block vs inode usage — always check both
df -h
df -i
# Largest directories / files on one filesystem
sudo du -xh <MOUNT> --max-depth=1 2>/dev/null | sort -rh | head
sudo find <MOUNT> -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
# Inode hotspot (millions of small files)
sudo find <MOUNT> -xdev -type f | cut -d/ -f1-5 | sort | uniq -c | sort -rn | head
# Deleted-but-open files holding space (df > du)
sudo lsof +L1
sudo lsof -nP | grep '(deleted)'
sudo truncate -s 0 /proc/<PID>/fd/<FD>
# Reserved-block check (ext4)
sudo tune2fs -l /dev/<DEV> | grep -Ei 'Reserved block count|Block count'
# journald reclaim
journalctl --disk-usage
sudo journalctl --vacuum-size=500M
# Clear a live log without orphaning the fd
sudo truncate -s 0 /var/log/<HUGE_LOG>
Conclusion
No space left on device (ENOSPC) means a write had nowhere to land — but “nowhere” can be empty data blocks or empty inodes, and the message is the same for both. Always check df -h and df -i first, then reconcile against du to catch deleted files a process still holds open. The usual root causes:
- The filesystem is genuinely full of data blocks.
- Inodes are exhausted by millions of small files while bytes remain free.
- One or a few very large files (unrotated logs, core dumps, images).
- Deleted-but-open files whose blocks a running process still holds.
- ext4 reserved-blocks make non-root writes fail before
dfhits zero. - Runaway systemd-journald or application logs filling
/var.
Identify the affected mount and which resource (blocks vs inodes) is exhausted, reclaim with truncate/vacuum, then fix rotation and monitoring so it does not recur.
Fixed it? Get 500 Linux Admins & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2mount: wrong fs type, bad option, bad superblock
- 3Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block
- 4gpg: keyserver receive failed: No data
- 5Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 6Docker 'failed to create shim task': How to Fix the containerd Runtime Error
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.