Where a VPS’s disk space goes
The most common sources, as of November 14, 2026:Before cleaning up: take a snapshot
A cleanup is quick. So is one deletion too many. Take a snapshot from the console before touching binlogs, Docker volumes or kernels. You keep a one-click way back. Backups remain your baseline protection.Diagnose a full disk
Which filesystem is full
Mounted on). Most of the time, it is /.
Blocks or inodes
A disk can be full whiledf -h shows free space. Every file uses one inode, and on ext4 their number is set when the filesystem is created. Millions of small files run out of inodes before they run out of space.
IUse% reads 100%, find the directory holding the most files:
sudo du --inodes -x -d 1 /var | sort -n | tail). The usual culprits: PHP sessions, application cache, mail queue.
Find the largest directories
-x option matters: it stays on the filesystem of / and skips other mounts.
For faster navigation, ncdu does the same interactively:
apt clean or dnf clean all (below) to make some room.
Deleted files that are still open
Problem. You deleted a large log withrm and df did not move. The file is gone from the tree, but a process still holds it open: the space only comes back when the file is closed. The same applies when du reports far less than df.
Fix. List open files whose last link was removed:
COMMAND gives the process, SIZE/OFF the size. Restart the service involved:
PID and FD (without the trailing letter, 4w becomes 4) come from the lsof output:
sudo lsof -nP +L1 no longer lists the file, and df -h / shows the space returned.
Shrink the systemd journal
Problem./var/log/journal takes several gigabytes.
Fix. Measure, then shrink:
journald.conf man page recommends a separate file in /etc/systemd/journald.conf.d/ rather than editing the main file:
journalctl --disk-usage stays under the chosen value over the following days.
Empty and rotate application logs
Problem. A file in/var/log or in an application directory grows forever.
Fix. Find it:
truncate rather than rm:
/etc/logrotate.d/:
copytruncate copies the file, then empties it in place: the application keeps writing to the same file. If it can reopen its logs on a signal, prefer a postrotate block that sends it.
Check. Debug mode simulates the rotation without changing anything:
Clear the package cache
Problem. Packages downloaded during updates stay on disk. Fix.sudo du -sh /var/cache/apt or sudo du -sh /var/cache/dnf is back to a few megabytes.
Remove old kernels
Problem. Each kernel update installs a new version next to the previous ones, with its modules in/lib/modules and its image in /boot.
Fix on Debian and Ubuntu. Simulate first, then remove:
autoremove also removes dependencies that have become orphans.
Fix on AlmaLinux and Rocky. dnf keeps a fixed number of versions of “installonly” packages, the kernel among them, set by installonly_limit in /etc/dnf/dnf.conf. To remove the extra versions now:
uname -r gives the running kernel, which is still listed by dpkg -l 'linux-image*' or rpm -q kernel.
Clean up Docker
Problem./var/lib/docker keeps growing: replaced images, stopped containers, build cache, container logs.
Fix. Measure by category:
json-file, does not rotate. Find the largest:
/etc/docker/daemon.json (merge with the existing content if the file exists):
docker compose up -d --force-recreate for a Compose project).
Check. docker system df shows a RECLAIMABLE column close to zero.
Purge MySQL and MariaDB binlogs
Problem./var/lib/mysql holds dozens of binlog.000123 or mysql-bin.000123 files.
Fix. Never delete them with rm: the server keeps an index of these files. Use SQL:
SHOW REPLICA STATUS\G): a stopped or lagging replica still needs the binlogs it has not read.
For automatic expiry, the variable depends on the server:
expire_logs_days was removed from MySQL in 8.2: on MySQL 8.4, only binlog_expire_logs_seconds exists.
Apply the value live, then make it permanent:
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'; returns 604800, and SHOW BINARY LOGS; goes back no further than seven days.
The ext4 root reserve
Problem.df shows 100% for your applications, yet root can still write. That is by design.
Explanation. By default, ext4 reserves 5% of blocks for privileged processes. The tune2fs man page gives the reason: limit fragmentation and keep system daemons running once regular users can no longer write. This reserve is what still lets you log in and clean up.
/, only do it to get out of an emergency, then restore the value:
The reserve only exists on ext2, ext3 and ext4.
tune2fs does not apply to XFS: findmnt tells you the type.When the disk is simply too small
Once the cleanup is done, if/ climbs back above 85% within a few weeks, your usage has simply grown. Pick a VPS plan with more storage, then extend the partition to use the new space.
When the VPS stops responding
Prevent it: a threshold alert
A full disk is visible long before it happens. A short script warns you first, for space and for inodes:mail command (bsd-mailx on Debian and Ubuntu, s-nail on AlmaLinux and Rocky) and a local mail server. Our SPF, DKIM, DMARC and reverse DNS guide covers deliverability.
Check. Run the script with a threshold of 1 to get a test message:
Common pitfalls
rm on an open log. The space is not returned while the process runs. Use truncate -s 0, or restart the service afterwards.
Deleting journal files by hand. journalctl --vacuum-size only removes archives, never the file being written. Let it do the job rather than running rm in /var/log/journal.
Files hidden under a mount point. Files written to a directory before a disk was mounted on it take space on / but stay invisible to du. Bind-mount / elsewhere to see them:
df -h shows free space and the error persists: look at df -i.
docker system prune --volumes in production. It deletes volumes unused at that moment, including the one of a database whose container is stopped.
Binlogs deleted by hand. The server keeps their list in an index file that rm does not update. Always go through PURGE BINARY LOGS.
Further reading
- Snapshots: a restore point before every risky cleanup
- Backups: the baseline protection for your data
- Extend a partition after moving to a larger plan
- KVM console and rescue mode: regain control of an unresponsive VPS
- VPS that won’t boot: rescue mode, step by step
- Automatic security updates: they need free space to install, too

