A full disk is one of the most common reasons a server stops working. Logs stop being written, databases refuse new data, and programs fail with errors that do not mention disk space at all. Knowing how to see what is using the space, and where, is a skill you will use often.
By the end you can check how full each file system is, find the directories that are using the space, list the disks a machine has, read what is mounted where, and work through a full disk calmly.
Disks, file systems and mounting
A disk is raw storage: a physical drive, or on a cloud server a virtual volume. Before it can hold files, it is usually split into partitions and each partition is formatted with a file system, the structure that keeps track of files, directories and free space. Common file systems on Linux are ext4 and xfs.
Windows gives each disk a letter. Linux has a single directory tree that starts at /, and each file system is attached to a directory in it. Attaching it is called mounting, and the directory is its mount point. A server might have its system disk mounted at / and a second disk for data mounted at /var/lib/postgresql. You move between them with cd and never notice the boundary, but each one has its own size and fills up on its own.
lsblk lists the block devices, which means disks and partitions, as a tree:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 20G 0 disk
├─nvme0n1p1 259:1 0 19.9G 0 part /
└─nvme0n1p15 259:2 0 106M 0 part /boot/efi
nvme1n1 259:3 0 100G 0 disk /data
This server has two disks. The first has two partitions, mounted at / and /boot/efi. The second disk is used whole, with no partitions, and is mounted at /data. In a Docker container lsblk shows the host's disks, which can look confusing; the container's own files sit on a layered file system called overlay.
How full is each file system: df
df (disk free) shows each mounted file system, how big it is, and how much is used. -h prints sizes in K, M and G instead of blocks:
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p1 20G 15G 4.2G 78% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
/dev/nvme0n1p15 105M 6.1M 99M 6% /boot/efi
/dev/nvme1n1 98G 41G 53G 44% /data
Read the Use% and Mounted on columns first. tmpfs lines are file systems held in memory, not on disk, and you can usually ignore them.
To check only the file system that holds a particular directory, pass it as an argument. -T adds the file system type:
$ df -hT /var/log
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p1 ext4 20G 15G 4.2G 78% /
/var/log is not a mount point of its own, so df reports the file system it lives on, which is /.
A file system can also run out of inodes, the entries that record each file, while it still has space. That happens with millions of tiny files. df -i shows inode use in the same layout; if IUse% is at 100%, nothing new can be created, whatever df -h says.
What is using the space: du
df tells you a file system is full. du (disk usage) tells you which files and directories are filling it. -s gives one total per argument and -h makes it readable:
$ du -sh /var/log
288K /var/log
To see which directories inside a directory are biggest, give du all of them and sort the result. sort -h understands sizes like 4.0K and 1.2G, and -r puts the largest first:
$ du -sh /var/* 2>/dev/null | sort -rh | head -5
4.0G /var/lib
1.2G /var/log
504K /var/cache
8.0K /var/tmp
4.0K /var/spool
2>/dev/null throws away the error messages for directories you cannot read; the Pipes and text processing topic explains it. Then repeat the same command one level down, into the largest directory, until you reach the files themselves:
$ du -sh /var/log/* 2>/dev/null | sort -rh | head -3
1.1G /var/log/app
96M /var/log/journal
1.2M /var/log/apt
To stay on one file system and not wander into other disks mounted below the directory you are measuring, add -x. find can also look for large files directly, as the Searching for files and text topic showed:
$ find / -xdev -type f -size +100M 2>/dev/null
/var/log/app/app.log
Mounting and /etc/fstab
mount with no arguments lists every mounted file system. The list is long, so findmnt is easier to read for one mount point:
$ findmnt /data
TARGET SOURCE FSTYPE OPTIONS
/data /dev/nvme1n1 xfs rw,relatime
To attach a file system, give mount the device and the mount point, which must be an existing directory. umount (no n) detaches it:
$ mkdir -p /mnt/backup
$ mount /dev/nvme2n1 /mnt/backup
$ umount /mnt/backup
A mount made by hand lasts until the next reboot. Mounts that should come back at every boot are listed in /etc/fstab, one per line, with six fields:
UUID=2c1f8a4e-5b0d-4e1a-9f2c-7d3e6b8a1c40 /data xfs defaults,nofail 0 2
| Field | Example | Meaning |
|---|---|---|
| Device | UUID=2c1f… | What to mount. A UUID is used because device names can change between boots |
| Mount point | /data | Where to attach it |
| Type | xfs | The file system type |
| Options | defaults,nofail | How to mount it; nofail lets the machine boot even if this disk is missing |
| Dump | 0 | Used by an old backup tool; almost always 0 |
| Check order | 2 | When to check the file system at boot: 1 for /, 2 for others, 0 for never |
lsblk -f shows each device's UUID. Be careful with this file: a mistake in it can stop a server from booting. After you change it, run mount -a to mount everything it lists, and fix any error before you reboot. The Docker container has an empty fstab, because the container does not boot.
Fixing a full disk
When a file system fills up, programs fail when they try to write:
$ echo test > /data/test.txt
bash: echo: write error: No space left on device
Work through it in order:
df -hto see which file system is full.du -sh <mount point>/* | sort -rh | headto find the largest directory, and repeat one level down until you find the files.- Decide what can go. Old, rotated logs like
app.log.1are usually safe to delete. A database's data files are not.
There is one trap. If you delete a file that a running program still has open, the name disappears but the space does not come back until the program closes it. du no longer counts the file, but df still does, so the two disagree:
$ rm /data/logs/app.log
$ df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 90M 88M 0 100% /data
$ du -sh /data
28M /data
You can find such files through /proc, where each process lists the files it has open:
$ find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null
/proc/18/fd/3 -> /data/logs/app.log (deleted)
Process 18 still holds the file. Restarting that program frees the space. To empty a log a program is still writing to, truncate it instead of deleting it:
$ : > /data/logs/app.log
: is a command that does nothing, so > leaves the file in place with zero bytes.
Tasks
Do these in your Linux shell.
Task 1: Find what is filling a directory
Create some test data in your home directory:
$ mkdir -p ~/data/logs ~/data/cache ~/data/notes
$ fallocate -l 300M ~/data/logs/app.log
$ dd if=/dev/zero of=~/data/cache/blob bs=1M count=50 status=none
$ echo "remember the milk" > ~/data/notes/todo.txt
fallocate and dd both create a file of a given size. Now use du and sort to list the directories in ~/data with the largest first, and find the single largest file. Then use df to see how much space is free on the file system that holds ~/data.
Check your work
$ du -sh ~/data/* | sort -rh
301M /root/data/logs
51M /root/data/cache
8.0K /root/data/notes
$ du -sh ~/data/logs/*
300M /root/data/logs/app.log
$ df -h ~/data
Filesystem Size Used Avail Use% Mounted on
overlay 59G 17G 40G 30% /
Your sizes on df will differ. If the order comes out as 8.0K, 51M, 301M, you used sort -r instead of sort -rh, which sorts the text instead of the sizes.
Task 2: Fill a disk and recover
This task needs a container that is allowed to mount file systems. Exit your container and start one with --privileged:
$ docker run -it --rm --privileged --hostname lab ubuntu:24.04
Make a 100 MB file, format it as ext4, and mount it at /mnt/data as if it were a small disk:
$ cd ~
$ fallocate -l 100M disk.img
$ mkfs.ext4 -q disk.img
$ mkdir -p /mnt/data
$ mount -o loop disk.img /mnt/data
Now create /mnt/data/logs, write a 60 MB app.log and a 60 MB app.log.1 into it with dd, and see what happens. Find out which file system is full and which file is largest, free the space by deleting the old log, and confirm with df. Finish with umount /mnt/data.
Check your work
$ df -hT /mnt/data
Filesystem Type Size Used Avail Use% Mounted on
/dev/loop0 ext4 90M 24K 83M 1% /mnt/data
$ mkdir /mnt/data/logs
$ dd if=/dev/zero of=/mnt/data/logs/app.log bs=1M count=60 status=none
$ dd if=/dev/zero of=/mnt/data/logs/app.log.1 bs=1M count=60 status=none
dd: error writing '/mnt/data/logs/app.log.1': No space left on device
$ df -h /mnt/data
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 90M 88M 0 100% /mnt/data
$ du -sh /mnt/data/logs/* | sort -rh
60M /mnt/data/logs/app.log
28M /mnt/data/logs/app.log.1
$ rm /mnt/data/logs/app.log.1
$ df -h /mnt/data
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 90M 61M 23M 73% /mnt/data
$ umount /mnt/data
The file system is smaller than 100 MB because ext4 keeps some of the space for its own records. The second file stopped at 28 MB, when the disk filled. If mount fails with mount failed: No such file or directory, the container was not started with --privileged.
Quiz
1. What is the difference between what df and du tell you?
Show answer
df reports how full each mounted file system is, as a whole. du adds up the size of the files and directories you point it at. Use df to find which file system is full, then du to find what is filling it.
2. You are on a server and an application logs No space left on device, but df -h shows / at 40%. What else should you check?
Show answer
Check the file system the application actually writes to, with df -h on its directory, because it may be a separate mount that is full. Then check inodes with df -i: a file system with no free inodes reports the same error even when it has space.
3. You deleted a 5 GB log file, but df shows the same usage as before. Why, and how do you get the space back?
Show answer
A running program still has the file open, so the space is not freed until the program closes it. Find the program through /proc/*/fd entries marked (deleted), or with lsof, and restart it. Next time, truncate the file with : > file instead of deleting it.
4. Why do lines in /etc/fstab usually name a disk by UUID= rather than by a name like /dev/sdb?
Show answer
Device names are given out in the order disks are found at boot, and they can change when a disk is added or removed. A UUID belongs to the file system itself, so the line always points at the same file system.