Linux VPS Disk and Inode Cleanup: du, find, lsof
No space left on device. The error stops your application cold, and the first instinct — delete some logs and move on — usually misses what is actually wrong. A full disk on a VPS is rarely one obvious huge file; it is more often a directory you forgot about, a log that a process is still writing to after you "deleted" it, or — the one that fools almost everyone the first time — a disk with plenty of free space that has run out of inodes. This guide walks the diagnosis in the order an experienced administrator actually does it, with the three tools that find every case: du, df, and lsof.
df -h to confirm which filesystem is full, then df -i to check whether it is space or inodes you have run out of. Locate large directories top-down with du -h --max-depth=1 / | sort -rh. If df and du disagree — df says full, du says there is room — the space is held by a deleted-but-still-open file; find it with lsof | grep deleted and restart the process holding it. Clear package cruft with apt autoremove and apt clean.Step 1: Confirm What Is Actually Full
Before deleting anything, find out which filesystem is the problem and by how much. A VPS often has several mounts, and the full one may not be /:
1df -h
Read the Use% column. A mount at 100% is your target; note its mount point, because every later command should focus there rather than wandering the whole tree. If /var is its own mount and it is full while / has room, that tells you the culprit is logs, databases, or container data — not your home directory.
Step 2: Rule Out Inode Exhaustion First
This is the check that saves an hour of confusion. Every file consumes one inode, the small filesystem record that stores a file's metadata. A filesystem has a fixed number of inodes set when it was created, and you can exhaust them while disk space sits half empty — typically from millions of tiny files: a runaway session directory, mail spool, or a cache that creates one file per request. The symptom is identical (No space left on device) but df -h shows free space, which makes it baffling until you look:
1df -i
If the IUse% column reads 100% on the affected mount, you have an inode problem, not a byte problem, and deleting a few large files will not help — you need to delete many small ones. Find which directory is hoarding inodes by counting files per subtree:
1sudo find /var -xdev -type f | cut -d/ -f1-4 | sort | uniq -c | sort -rn | head
The -xdev flag keeps find from crossing into other filesystems, so the count reflects only the full mount. The directory at the top of the list is where your millions of files live — usually a cache or spool that needs its retention fixed, not just a one-time purge.
Step 3: Find the Space Hogs Top-Down
For an ordinary out-of-space situation, walk the tree from the top and let du point you down. The key is to descend one level at a time rather than dumping the entire filesystem:
1sudo du -h --max-depth=1 / | sort -rh | head
--max-depth=1 summarises each immediate child of / as a single total; sort -rh orders them largest-first using human-readable sizes. Pick the biggest entry, then run the same command one level deeper on it:
1sudo du -h --max-depth=1 /var | sort -rh | head
Repeat, following the largest directory downward, and within a few steps you arrive at the actual offender — almost always /var/log, /var/lib, a database directory, or an application's upload folder. This top-down descent is faster and far less noisy than asking du to list every file on the disk at once.
To pinpoint individual large files once you are in the right neighbourhood, find filters by size directly:
1sudo find /var -xdev -type f -size +100M -exec ls -lh {} \;
That lists every file over 100 MB under /var, with human-readable sizes, so a single oversized log or forgotten database dump stands out immediately.
Step 4: When df and du Disagree — Deleted-but-Open Files
Here is the case that defeats most cleanup attempts. You delete a 5 GB log file, df still shows the disk full, and du now shows plenty of space used elsewhere — the numbers do not add up. The reason is fundamental to how Unix works: deleting a file only removes its directory entry. As long as a running process still holds the file open, the kernel keeps the data on disk, and the space is not freed until that process closes the handle or exits. A web server or logging daemon that has an old, rotated logfile open will hold gigabytes hostage this way.
lsof (list open files) finds them. Deleted-but-open files are flagged (deleted) in its output:
1sudo lsof | grep deleted
Each matching line shows the process name, its PID, and the size of the phantom file. To reclaim the space, you do not need to reboot — restart (or signal) the process holding the handle:
1sudo systemctl restart nginx
The moment the process releases the descriptor, the kernel frees the blocks and df drops accordingly. This single trick resolves a large share of "the disk is full but I can't find what's using it" tickets. It is also the argument for proper log rotation with a postrotate signal — so a daemon reopens its logfile instead of clinging to the deleted one.
Step 5: Reclaim the Easy Wins
Once the real culprit is handled, clear the routine cruft that accumulates on any long-lived server. On Debian and Ubuntu, the package manager is the usual quiet consumer:
1sudo apt autoremove --purge
2sudo apt clean
apt autoremove --purge removes packages — notably old kernels under /boot, a frequent cause of a full /boot partition — that were installed as dependencies and are no longer needed, and --purge drops their config files too. apt clean empties the local cache of downloaded .deb archives under /var/cache/apt, which can quietly reach hundreds of megabytes. If you run Docker, an unused-image purge is often the single biggest reclaim on the box:
1docker system prune -a
Be deliberate with that one — it removes all images not tied to a running container — but on a server that has built images repeatedly it routinely frees gigabytes.
A Repeatable Diagnosis
The order is what makes this reliable: df -h to find the full mount, df -i to rule out inodes, du --max-depth=1 to descend to the offender, lsof | grep deleted when the numbers refuse to add up, and the package and image caches for the easy reclaim. Skipping the inode check or jumping straight to deleting files is how cleanups fail and outages drag on. Walk the steps in sequence and a full-disk emergency becomes a five-minute diagnosis with a clear, evidence-backed fix — and the inode and deleted-file checks mean you will catch the two cases that quietly defeat everyone else.
References
Posts in this series
- Linux VPS Performance Tuning: sysctl & swap 2026
- Systemd Service Hardening: Sandbox a Unit (2026)
- Fail2ban on Ubuntu VPS: Stop SSH Brute Force 2026
- UFW Firewall Rules for a Public VPS: 2026 Setup
- How to Harden SSH on an Ubuntu VPS (2026 Guide)
- Automated Backups with Rsync and Cron on Linux 2026
- TLS Certificates with Certbot on an Ubuntu VPS 2026
- Harden Nginx on Ubuntu: Headers, TLS & Limits 2026
- Automatic Security Updates on Ubuntu: 2026 Setup
- Logrotate for Nginx and App Logs on a Linux VPS
- Systemd Timers: Replace Cron Jobs on a Linux VPS
- Manage Linux Users and sudo Permissions on a VPS
- Linux VPS Disk and Inode Cleanup: du, find, lsof
- journald Logs: Retention, Size Limits and Queries