To check disk usage on Linux, run df -h to see how full each filesystem is, then du to find the directories that use the space and find to list the largest files. ncdu does the last two interactively. Four commands cover almost every case:
df -h # free space per filesystem
sudo du -xh --max-depth=1 / 2>/dev/null | sort -h # biggest top-level directories, last
sudo find / -xdev -type f -size +100M -exec ls -lh {} + 2>/dev/null | sort -k5 -h # files over 100 MB
sudo ncdu -x / # interactive browser (install it first)This is the routine check-up. If writes are already failing with “No space left on device”, go to No space left on device first: it covers full inodes and deleted files that still hold their space, which du cannot see.
How much disk space is left?
df -h # sizes in K, M and G
df -hT # plus the filesystem type
df -h /var/lib/mysql # only the filesystem that holds this path
df -i # inodes: how many files fit, not how many bytes
df -h -x tmpfs -x devtmpfs -x overlay -x squashfs # hide the virtual rowsRead the line mounted on / first, then any separate data disk. Several rows are not disks at all and can be ignored:
tmpfsanddevtmpfs(/run,/dev/shm,/dev) live in memory.overlayrows belong to Docker containers and only repeat the numbers of the disk underneath./snap/…loop devices on Ubuntu are read-only images and always show 100%. That is normal.
If Used and Avail do not add up to Size, that is not an error either. ext4 reserves 5% of a filesystem for root by default, so ordinary users and services hit “full” while root still has a little room to clean up.
Which directory is using the space?
Start at the top and drill down one level at a time. -x keeps du on one filesystem, so it skips /proc, other disks and network mounts; sort -h understands sizes like 12K and 3.4G and puts the biggest last.
sudo du -xh -d1 / 2>/dev/null | sort -h # -d1 is short for --max-depth=1
sudo du -xh -d1 /var 2>/dev/null | sort -h # then the biggest one, and so on
sudo du -sh /var/log /var/lib/docker /home # one total per path
du -sh --exclude=node_modules ~/projects # leave matching names outRun du with sudo: without it, every directory you may not read counts as empty and the totals come out too low. On Debian and Ubuntu, /var is usually the big one, with logs in /var/log, databases in /var/lib/mysql or /var/lib/postgresql and Docker in /var/lib/docker.
How do I use ncdu?
ncdu (NCurses Disk Usage) scans a directory once and lets you browse the result sorted by size, which is faster than running du again for every level.
sudo apt install ncdu # Debian, Ubuntu
sudo dnf install epel-release && sudo dnf install ncdu # Rocky Linux, AlmaLinux (EPEL)
sudo ncdu -x / # scan the root filesystem only
sudo ncdu -rx / # read-only: the delete key is disabled- Arrow keys and Enter move and open a directory; the left arrow goes back up.
ddeletes the selected file or directory after a confirmation. It deletes for real, so use-rwhile you are only looking, especially on a production server.s/nsort by size or name,gswitches between percentage and graph,ishows details of the selected item andqquits.
On a big disk you can scan once and look later: sudo ncdu -x -o /tmp/scan.ncdu / writes the result to a file, ncdu -f /tmp/scan.ncdu opens it.
How do I find the largest files?
# every file over 100 MB on the root filesystem, biggest last
sudo find / -xdev -type f -size +100M -exec ls -lh {} + 2>/dev/null | sort -k5 -h
# the 20 largest files, with exact sizes
sudo find / -xdev -type f -printf '%s\t%p\n' 2>/dev/null | sort -nr | head -20 | numfmt --to=iec --field=1
# files over 1 GB under /home and /srv
sudo find /home /srv -type f -size +1G -exec ls -lh {} +
# files over 50 MB that have not changed in 30 days
sudo find /var /home -xdev -type f -size +50M -mtime +30 -exec ls -lh {} +-size +1G means larger than 1 GiB; the units k, M and G are powers of 1024. -xdev stays on one filesystem, like du -x. -mtime +30 matches files last modified more than 30 days ago, a good filter for forgotten archives and dumps.
Why do df and du show different numbers?
df asks the filesystem how many blocks are in use; du adds up the files it can see. A gap between the two usually means space held by a file that was deleted while a process still had it open, often a log. sudo lsof +L1 lists such files, and restarting the process frees the space; the deleted-but-open section of the ENOSPC guide shows how. Smaller gaps come from files hidden under a mount point, from directories du could not read without sudo, and from filesystem metadata.
What can I safely clean up on a VPS?
In roughly the order they pay off on a typical server. Data directories such as /var/lib/mysql and anything in /boot are not on this list on purpose.
The systemd journal
journalctl --disk-usage # "Archived and active journals take up 3.9G"
sudo journalctl --rotate # close the active file so it can be vacuumed too
sudo journalctl --vacuum-size=200M # or: --vacuum-time=2weeksTo keep it at that size, set a limit in a drop-in file and restart journald:
sudo mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=200M\n' | sudo tee /etc/systemd/journald.conf.d/size.conf
sudo systemctl restart systemd-journaldOld logs in /var/log
sudo du -sh /var/log/* 2>/dev/null | sort -h | tail
sudo find /var/log -type f \( -name '*.gz' -o -name '*.[0-9]' \) -delete # rotated copies only
sudo truncate -s 0 /var/log/myapp/huge.log # empty a live logEmpty a log that is still being written with truncate rather than deleting it: after rm, the program keeps writing to the deleted file and the space does not come back until it restarts. A single log that keeps growing points to a missing logrotate rule in /etc/logrotate.d/ or to an application logging at debug level.
The package cache and old kernels
# Debian, Ubuntu
sudo apt clean # downloaded .deb files in /var/cache/apt/archives
sudo apt autoremove --purge # unused dependencies, including old kernels
# Rocky Linux, AlmaLinux
sudo dnf clean all
sudo dnf remove --oldinstallonly # old kernels; keeps the newest and the running oneBoth keep the running kernel and a fallback. Never delete files in /boot by hand: removing the wrong kernel or initramfs leaves a server that does not boot.
Docker images, build cache and container logs
docker system df # images, containers, volumes and build cache
docker image prune -a # images no container uses
docker builder prune # build cache
docker container prune # stopped containers
# the five largest container logs
sudo sh -c 'du -sh /var/lib/docker/containers/*/*-json.log' | sort -h | tail -5Container logs have no size limit by default and are a classic cause of a full disk on a long-running server. Cap them in /etc/docker/daemon.json, restart Docker, then recreate your containers, because the limit only applies to containers created afterwards:
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}Old snap revisions on Ubuntu
snapd keeps disabled older revisions of every snap. List and remove them, and tell snapd to keep only two in future:
snap list --all | awk '/disabled/ {print $1, $3}'
snap list --all | awk '/disabled/ {print $1, $3}' | while read -r name rev; do
sudo snap remove "$name" --revision="$rev"
done
sudo snap set system refresh.retain=2Old backups, dumps and archives
sudo find / -xdev -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.sql' -o -name '*.sql.gz' \) \
-size +100M -exec ls -lh {} + 2>/dev/nullBackups on the same disk as the data protect against very little, so move them to another machine rather than keeping them here. tar -tzf archive.tar.gz | less lists what an archive holds before you delete it; the tar.gz guide has the rest, and transferring files with scp and rsync shows how to copy them off the server.
Caches in home directories
du -sh ~/.cache ~/.npm 2>/dev/null
pip cache purge
npm cache clean --forceThe swap file
A /swapfile of several gigabytes shows up near the top of every du and find list. It is not waste, and deleting it while it is active does not free the space anyway. swapon --show tells you whether and how big it is; to make it smaller, turn it off, recreate it and turn it on again as described in adding swap space on Linux.
How do I get warned before the disk fills up?
A three-line script and a cron entry are enough. The script prints a line for every filesystem at or above 85% and nothing otherwise:
#!/bin/sh
df -P -x tmpfs -x devtmpfs -x overlay -x squashfs |
awk 'NR > 1 { sub(/%/, "", $5); if ($5 + 0 >= 85) print $6 " is at " $5 "%" }'# cron mails output only when there is some, so this mails only when a disk is over 85 %
MAILTO=you@example.com
*/30 * * * * /usr/local/bin/disk-alert
# no mail server? write it to the system log instead
# */30 * * * * /usr/local/bin/disk-alert | logger -t disk-alertMake the script executable with sudo chmod +x /usr/local/bin/disk-alert. Mail needs a working mail transfer agent on the server; without one, cron discards the output. If the check never seems to run, see cron job not running.
What about a Windows server?
- See the free space: File Explorer → This PC, or
Get-PSDrive Cin PowerShell. - Find what uses it: WinDirStat or TreeSize Free show folders by size, the way
ncdudoes. - Clean up: Disk Cleanup (
cleanmgr) with “Clean up system files”, andDism.exe /Online /Cleanup-Image /StartComponentCleanupin an elevated prompt for superseded Windows Update files.
Windows Server needs far more room than a Linux server doing the same job. Plan on 40 GB or more; on 20 GB, updates alone keep the disk close to full.
When is cleaning up not enough?
If every cleanup only buys a few weeks, the disk is too small for the workload, and the next full disk will arrive at a worse moment. Standard KVM plans run from 20 GB to 280 GB of replicated enterprise storage, so moving up a size or two is usually cheaper than the time spent trimming logs every month. If the server is short of disk and nothing else, a storage block adds 15 GB for €1.00 a month on any KVM plan, without a plan change.
Frequently asked
Why do df and du show different numbers?
df counts the blocks the filesystem has in use, du adds up the files it can see. The usual gap is a file that was deleted while a process still had it open, often a log: 'sudo lsof +L1' lists them, and restarting the process frees the space. Files hidden under a mount point and directories du could not read without sudo explain smaller differences.
Is it safe to delete files in /var/log?
Rotated copies are safe to delete: files ending in .gz or a number, such as syslog.2.gz or auth.log.1. Empty a log that is still being written with 'sudo truncate -s 0 file' instead of deleting it, because a deleted log keeps its space until the program restarts. Shrink the systemd journal with 'journalctl --vacuum-size=200M' rather than deleting files under /var/log/journal, and keep the directories themselves, which some services need in order to start.
How do I find files larger than 1 GB on Linux?
Run 'sudo find / -xdev -type f -size +1G -exec ls -lh {} +'. -xdev keeps find on the root filesystem, -size +1G matches files larger than 1 GiB, and ls -lh prints each one with a readable size. Replace / with a directory such as /home to search only there, or use +100M for a lower threshold.
What is ncdu?
ncdu (NCurses Disk Usage) is an interactive disk usage browser for the terminal. It scans a directory once, then lets you move through the tree sorted by size and delete files with the d key. Install it with 'sudo apt install ncdu' on Debian and Ubuntu or from EPEL on Rocky Linux and AlmaLinux, and run 'sudo ncdu -x /'; 'ncdu -r' opens it read-only.