Disk full on Linux: how to find and free up space safely
When a Linux server runs out of disk space, things break in unpredictable ways: databases refuse writes, logs go silent, package upgrades die halfway. This guide is for anyone who administers a Linux server or VPS. It shows how to find what actually eats the space, how to free it safely, and which files you should never touch.
First, confirm what is actually full
Start with two commands:
df -h df -i
The first shows used space per filesystem, the second shows inode usage. If df -h reports free gigabytes but applications still throw "No space left on device", you have most likely run out of inodes, not bytes. That is almost always caused by millions of tiny files: PHP sessions, cache shards, queue fragments. In that case the fix is deleting the file flood, and a bigger disk would not help.
Also note which mount point is full. A full /var on its own partition needs a different cleanup path than a full root filesystem, and cleaning the wrong one achieves nothing.
df says full, du disagrees: deleted but open files
A classic trap: du -sh / finds 40 GB of files, while df insists 75 GB are used. The difference is usually space held by files that were deleted while some process still keeps them open. The kernel cannot release the blocks until the last file descriptor closes. The typical story: someone removed a huge log with rm, but the service kept writing into the now invisible file.
lsof -nP +L1
This lists open files with a link count of zero, together with the process and file descriptor holding them. Restarting or reloading the service releases the space immediately:
systemctl restart nginx
If you cannot restart the service right now, truncate the deleted file through /proc, using the PID and FD number from the lsof output:
: > /proc/1234/fd/4
One more cause of the same symptom: data hidden under a mount point. If a backup job once wrote to /mnt/backup while the backup disk was not mounted, those files sit on the root filesystem, invisible while a disk is mounted over them. Unmount and look, or bind mount / to another location and inspect it there.
Walk the tree with ncdu
For everything else, ncdu is the fastest way to see where the space went:
apt install ncdu ncdu -x /
The -x flag keeps it on one filesystem, so network mounts and other partitions do not pollute the numbers. Navigation is arrow keys, directories are sorted by size, and pressing d deletes the selected item, so be deliberate with that key. If the disk is so full that package installs fail, plain du does the same job with more typing:
du -xh --max-depth=1 / 2>/dev/null | sort -h
Descend into the largest directory and repeat until you find the culprit.
The usual suspects and how to clean them safely
Journald and application logs
journalctl --disk-usage journalctl --vacuum-size=200M
Vacuuming only removes archived journal entries, so it is safe. To cap the journal permanently, set SystemMaxUse=200M in /etc/systemd/journald.conf and restart systemd-journald. For plain files in /var/log, look for one log growing much faster than the rest: a runaway log means some application is failing in a loop, and the real fix is there, not in deleting evidence. Check that logrotate actually runs, and never delete an open log file, truncate it instead:
truncate -s 0 /var/log/huge.log
Apt cache and old kernels
du -sh /var/cache/apt apt-get clean apt-get autoremove --purge
apt-get clean is always safe: it only removes downloaded package files. autoremove --purge also removes old kernels, which often occupy several gigabytes in /boot and /usr/lib/modules. Before you confirm, run uname -r and make sure the running kernel is not on the removal list. Keep the current kernel and one known good fallback.
Docker images, containers and volumes
docker system df docker system prune
The first command shows what images, containers, volumes and build cache cost. Plain prune removes stopped containers, unused networks and dangling images, which is normally safe. The dangerous variants are prune -a, which deletes every image not used by a running container, and anything touching volumes. Volumes are where databases keep their data: run docker volume ls and only remove a volume when you can say what it belongs to.
Core dumps and mail queues
coredumpctl list du -sh /var/lib/systemd/coredump /var/crash
A crashing binary can leave gigabytes of dumps. Old ones can go; to stop the growth, cap them in /etc/systemd/coredump.conf and fix whatever keeps crashing. Also look for core.* files in application working directories.
mailq | tail -1 du -sh /var/spool/postfix
A stuffed mail queue usually means an abused web form or a script mailing errors in a loop. Fix the source first. postsuper -d ALL then empties the queue, but it deletes legitimate outgoing mail too, so read the queue before flushing it.
What not to delete
- Database directories such as
/var/lib/mysqlor/var/lib/postgresql. Free space there by cleaning data through the database itself, never withrm. - Anything inside
/var/lib/dockerby hand. Use docker commands, or you corrupt the storage driver state. /proc/kcoreand other files under/procor/sys. They look huge but occupy no disk space at all.- The running kernel or its modules directory.
- Open log files. Deleting them just recreates the invisible open file problem described above.
When in doubt, move the file to another filesystem instead of deleting it, confirm nothing broke, then delete.
Prevent the next incident
Set a monitoring alert at 80 percent usage, so you fix things calmly instead of at 100 percent. Cap journald, verify that logrotate covers every log your applications write, and put apt-get clean plus a conservative docker prune into a monthly routine you run consciously, not blindly from cron. And if the data is simply growing because the project is growing, cleanup is the wrong tool: a disk that sits permanently above 90 percent needs more space, not more deleting. On our VPS servers in the Riga data centre you can extend disk space without reinstalling, which is usually cheaper than the hours spent hunting the last gigabyte.