How Linux works · Hands-on lab

Storage, Filesystems & LVM

Disks, partitions, filesystems, and mounts, and how to find what's filling a disk. Build an LVM volume and grow it while it's in use, then solve the classic puzzle where df says the disk is full but du can't find the files.

Module: 10 of 25 Level: How Linux works · Hands-on lab Time: 17 min read Published:
All modules in this track (25)

In this module, you'll learn to:

  • Map disks, partitions, filesystems, and mount points with lsblk, df, and findmnt
  • Find what's using disk space, and recognise when inodes run out instead
  • Create an LVM volume, grow it without unmounting, and recover space held by deleted files

Before you start: finish Files, Inodes & Permissions and The Boot Process. Open a shell on your lab VM with multipass shell lab.

Layers

From a disk to a directory

Storage on Linux is built in layers. Knowing which layer you're looking at is half of every storage problem.

The layers between a disk and a directory
Block devicea whole disk: /dev/sda or /dev/vda on most VMs, /dev/nvme0n1 on NVMe and newer cloud instances
divided into partitions, or handed to LVM
Partition or logical volume/dev/sda1, or /dev/datavg/datalv
formatted with a filesystem
Filesystemext4 or xfs: organises inodes, directories, and data blocks
mounted at a directory
Mount point/data: where the filesystem appears in the single directory tree
lsblk                  # disks and partitions, and where each is mounted
lsblk -f               # plus filesystem type, label, and UUID
findmnt                # every mount, as a tree
df -hT                 # space used and free per filesystem, with its type
Scroll sideways to see the full diagram →

ext4 is the default on Ubuntu and Debian. xfs is the default on RHEL family systems and handles very large files well. One practical difference: ext4 can be shrunk, xfs can only grow.

Space

What's filling the disk

df answers how full is each filesystem. du answers which directories use the space. Together they find almost every full disk:

df -h                                          # which filesystem is full?
sudo du -xh --max-depth=1 / 2>/dev/null | sort -h | tail -8
                                               # biggest directories on that filesystem
sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail -8
                                               # then go one level deeper, and repeat
Scroll sideways to see the full diagram →

-x keeps du on one filesystem, so it doesn't wander into other mounts. The usual culprits are logs in /var/log, container images in /var/lib/docker or /var/lib/containerd, old kernels, and forgotten backups.

A filesystem can also run out of inodes while plenty of space remains, as Files, Inodes & Permissions explained. Every new file then fails with "No space left on device", even though df -h shows free space:

df -i                  # inode use per filesystem; look for 100% IUse
Scroll sideways to see the full diagram →
Mounts

Temporary and permanent

mount attaches a filesystem now; /etc/fstab attaches it at every boot.

# device                                    mount point  type  options           dump pass
UUID=0a1b2c3d-1111-2222-3333-444455556666   /data        ext4  defaults,nofail   0    2
Scroll sideways to see the full diagram →
FieldMeaning
DeviceWhich filesystem. UUID= is safest: disk names like /dev/sdb can change between boots
Mount pointThe directory where it appears. It must already exist
Typeext4, xfs, nfs, and so on
Optionsdefaults, plus extras such as nofail (boot even if it's missing), noatime (fewer writes), ro (read-only), noexec (no programs run from it)
DumpLegacy backup flag; always 0
PassThe order fsck checks filesystems at boot: 1 for root, 2 for others, 0 to skip

After any change: sudo findmnt --verify and sudo mount -a, before you reboot.

Field note

A mount that stopped answering

This one happened while this track was being written, on a workstation rather than a server, but the lesson is the same one production teams meet with NFS and EFS.

A Linux terminal running under WSL (Linux on Windows) refused to open a project folder on the D: drive. It failed with chdir(/mnt/d/...) failed 5 and dropped into / instead. Every path under /mnt/d returned Input/output error, even ls /mnt/d itself. Meanwhile Windows opened the same folder normally, and its permissions looked normal. So the disk and the files were fine. What had broken was the mount: WSL reaches Windows drives through a network-style filesystem mounted at /mnt/d, and that connection had failed.

# Is the failing path inside a mount, and what is it?
findmnt -T /mnt/d/project      # the mount that contains this path, its source and options
ls /mnt/d                      # Input/output error: the mount is broken, not the files

# Copy the options from a healthy mount of the same kind
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS /mnt/c

# Recover: detach the broken mount now, then mount it again
sudo umount -l /mnt/d
sudo mount -t drvfs D: /mnt/d -o uid=1000,gid=1000
Scroll sideways to see the full diagram →

umount -l is a lazy unmount: it detaches the mount from the directory tree immediately, and finishes cleaning up once no process is still using it. That's what lets you recover a mount that's too broken to unmount normally.

The first remount used different options, and every file came back owned by root instead of the user. Git refuses to work in a repository owned by another user ("dubious ownership"), so that would have looked like a brand-new problem. Copying the options from the healthy C: drive mount (uid=1000,gid=1000) fixed it. For mounts listed in /etc/fstab, sudo mount /path remounts with the options recorded there, which avoids this mistake entirely.

On servers, the same pattern shows up with network filesystems. An NFS or EFS mount can go stale after a network interruption or a change on the file server: commands fail with Stale file handle, or processes touching the mount hang in D state, as in Processes, Signals & File Descriptors. The approach doesn't change:

  • When every path under one directory fails and the rest of the system is fine, suspect the mount, not the files. findmnt -T <path> shows which mount you're actually in.
  • Check the other side first: is the file server reachable, and is the export still there?
  • Remount with the original options, ideally from /etc/fstab, and check ownership afterwards. A remount that "works" with the wrong options causes the next problem.
LVM

Volumes you can grow while they're in use

LVM (Logical Volume Manager) puts a flexible layer between disks and filesystems. Disks become physical volumes (PVs), PVs are pooled into a volume group (VG), and you carve logical volumes (LVs) out of the pool. When a volume fills up, you add a disk to the pool and grow the volume, with the filesystem still mounted.

The lab uses loop devices, ordinary files that act as disks, so you can practise without attaching real ones:

sudo apt install -y lvm2
truncate -s 1G ~/disk1.img ~/disk2.img
LOOP1=$(sudo losetup -f --show ~/disk1.img)
LOOP2=$(sudo losetup -f --show ~/disk2.img)
echo "$LOOP1 $LOOP2"

# One disk into a pool, one volume using all of it, an ext4 filesystem on top
sudo pvcreate "$LOOP1"
sudo vgcreate datavg "$LOOP1"
sudo lvcreate -n datalv -l 100%FREE datavg
sudo mkfs.ext4 /dev/datavg/datalv
sudo mkdir -p /mnt/data
sudo mount /dev/datavg/datalv /mnt/data
df -h /mnt/data                 # about 1 GB

# The volume is filling up: add the second disk and grow, while mounted
sudo pvcreate "$LOOP2"
sudo vgextend datavg "$LOOP2"
sudo lvextend -r -l +100%FREE /dev/datavg/datalv
df -h /mnt/data                 # about 2 GB, with no unmount and no downtime

sudo pvs; sudo vgs; sudo lvs    # the three layers, summarised
Scroll sideways to see the full diagram →

-r on lvextend resizes the filesystem in the same step. Without it, the volume grows but the filesystem inside doesn't, and df shows no change. Cloud disks work the same way: after enlarging an EBS volume, for example, you still grow the partition and the filesystem inside the server.

Break it, fix it

Full disk, empty directory

Monitoring alerts that /mnt/data is about 80% full. Someone already deleted the big log file to free space, yet the alert hasn't cleared. Reproduce it on the volume from the lab:

sudo dd if=/dev/zero of=/mnt/data/app.log bs=1M count=1500
sudo systemd-run --unit=applog bash -c 'exec 3>>/mnt/data/app.log; sleep infinity'
sudo rm /mnt/data/app.log

df -h /mnt/data            # still about 80% used
sudo du -sh /mnt/data      # almost nothing
Scroll sideways to see the full diagram →

Your job: explain the difference between the two numbers and get the space back. Try it before opening a hint.

Hint 1 · Where to look
  • Remember what deleting a file really does: it removes a name. Ask when the space behind that name is actually freed, and whether anything might still be using the file.
Hint 2 · Which tool
  • sudo apt install -y lsof, then sudo lsof +L1. It lists open files whose link count is below 1, meaning files that have been deleted but are still held open. Note the process, its PID, and the size.
Hint 3 · The cause and the fix
  • Cause: the applog service still has app.log open. Deleting the file removed its name, so du, which walks names, can't see it. But the inode and its 1.5 GB stay allocated until the last process closes it, and df reports the space as used.
  • Fix: restart or stop whatever holds it, here sudo systemctl stop applog. df drops immediately. If you can't restart a critical service, you can empty the file through the process instead: sudo truncate -s 0 /proc/<PID>/fd/3, using the PID and descriptor number from lsof.
  • Prevent it: don't delete logs that are in use. Truncate them (sudo truncate -s 0 file), or let logrotate rotate them and signal the service to reopen its log, which Part 3 covers.

When you're done, remove the lab volume:

sudo umount /mnt/data
sudo lvremove -y datavg/datalv
sudo vgremove datavg
sudo pvremove "$LOOP1" "$LOOP2"
sudo losetup -d "$LOOP1" "$LOOP2"
rm ~/disk1.img ~/disk2.img
Implementation notes
  • ext4 keeps 5% of space for root by default, so a filesystem can look "full" to normal users while root can still write. That reserve is what lets you sign in and fix a full disk.
  • A filesystem that turns read-only on its own usually means the kernel saw I/O errors and protected your data. Check sudo dmesg -T for disk errors before remounting it read-write.
  • Grow, don't move. With LVM, or a cloud volume resize, growing a full disk is minutes of work with no downtime. Plan volumes so the data that grows sits on its own volume, not on the root disk.
  • Loop devices don't survive a reboot, which is fine for a lab and why real volumes are real disks.
Recap

Key terms

  • Block device: a disk, or something that acts like one, such as /dev/sda.
  • Filesystem: the structure on a device that holds inodes, directories, and data. ext4 and xfs are the common ones.
  • Mount point: the directory where a filesystem appears in the tree.
  • df and du: space per filesystem, and space per directory.
  • Inode exhaustion: no inodes left for new files, while space remains.
  • LVM (PV, VG, LV): disks pooled into a group and carved into volumes you can grow.
  • Lazy unmount (umount -l): detach a mount now, and finish cleaning up once nothing is using it; the way to recover a broken mount.
  • Deleted-but-open file: a file with no name left, whose space stays used until the last process closes it.
Check yourself

Pop quiz

Five questions: three on the ideas in this module, and two scenarios where you apply them. The order changes every time you take it, and 4 out of 5 passes.

5 questions. Decide on your answer to each, then check it.

  1. Concept

    Why identify disks by UUID= in /etc/fstab instead of /dev/sdb?

    1. UUIDs mount faster
    2. Device names can change between boots; a filesystem's UUID doesn't
    3. /dev/sdb only works for xfs
    4. UUIDs are required for LVM
    Show answer

    Device names can change between boots; a filesystem's UUID doesn't

    Adding or removing a disk, or a different detection order, can rename /dev/sdb. The UUID belongs to the filesystem itself, so the right one is always mounted.

  2. Concept

    What does lvextend -r do that plain lvextend doesn't?

    1. Reboots after resizing
    2. Removes the old volume
    3. Resizes the filesystem inside the volume in the same step
    4. Adds a new disk to the volume group
    Show answer

    Resizes the filesystem inside the volume in the same step

    Growing the volume only makes room. -r also grows the filesystem, so the new space actually shows up in df.

  3. Concept

    df -h shows 40% used, but creating any new file fails with "No space left on device". What do you check?

    1. The page cache
    2. df -i, because the filesystem may have run out of inodes
    3. The swap usage
    4. The open-file limit
    Show answer

    df -i, because the filesystem may have run out of inodes

    Each file needs an inode, and a filesystem has a fixed number of them. Millions of tiny files can use them all while space remains.

  4. Scenario

    df shows a filesystem at 95%, but du on it adds up to only 40%. What's the most likely cause?

    1. du is broken
    2. The filesystem is corrupt
    3. Deleted files are still held open by a running process
    4. The journal is using the space
    Show answer

    Deleted files are still held open by a running process

    Space behind a deleted file is freed only when the last process closes it. sudo lsof +L1 finds the holder, and restarting it frees the space.

  5. Scenario

    The application's data volume is almost full, and it's on LVM with free disks available. How do you add space with no downtime?

    1. Unmount it, copy the data to a bigger disk, and remount it
    2. Add a disk to the volume group, then lvextend -r the volume while it stays mounted
    3. Delete the oldest files until there's room
    4. Create a new filesystem and move the app to it
    Show answer

    Add a disk to the volume group, then lvextend -r the volume while it stays mounted

    LVM grows the volume and the filesystem in place. ext4 and xfs both grow while mounted, so the application keeps running.

Questions about this learning path?

I'm happy to go into more detail on any of this: how to practise troubleshooting safely, which fundamentals matter most early on, or how a break-fix lab is put together.

anupojuprudhvi@gmail.com