Module 12 — Storage, Disks, and Filesystems

Updated 2 September 2026

Module 12 — Storage, Disks, and Filesystems

"The disk is full" is the most common Linux incident there is, and it has a dozen causes that look identical until you know how storage actually works. This module builds the whole stack from the bottom: block devices → partitions → filesystems → mounts → the abstractions (LVM, swap) on top. You'll learn to triage a full disk in under a minute, and to understand the counter-intuitive failures — space that won't free, "disk full" with space to spare — that stump people who only memorized df.

Legend used throughout: 🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)

Before you start

You need Modules Module 1 — What Linux Is–Module 11 — systemd and Services — especially inodes and the file-vs-name distinction (Module 3, the key to two of this module's hardest ideas), the filesystem tree and /dev//proc (Module 2), processes holding files open (Module 8), and pipes/sort (Modules 5–7). Tools: an Ubuntu 24.04 machine. Read-only exploration (df, du, lsblk, free, findmnt) is safe to run anywhere, including inside containers. The mounting, partitioning, and LVM exercises change disks and are marked — do those only on a throwaway VM or a spare disk you can afford to wipe, never on a machine whose data you need.

Goes deeper: the kernel side of storage — the block layer, page cache, and I/O — is the companion track's Module 10 — I/O, the Block Layer & iowait.

This page uses Mermaid diagram blocks. Notion shows them as code by default — click the block and set it to Preview to see the diagram. This reminder appears once per page.

Part A — The storage stack

A1. Block devices — what a disk is to Linux

Official docs: lsblk(8)

🧠 To Linux, a disk is a block device: a thing you read and write in fixed-size blocks, exposed as a special file under /dev. Real disks get names like /dev/sda, /dev/vda (virtio disks on VMs), or /dev/nvme0n1 (NVMe SSDs); their partitions append a number or p: /dev/sda1, /dev/nvme0n1p1. A block device is raw storage — a numbered sequence of blocks with no notion of files yet. Turning it into something you can store files on takes two more layers (partitions, then a filesystem), which the rest of Part A builds. The tool that shows the whole picture is lsblk (list block devices).

Real-world analogy — the empty warehouse

A block device is an empty warehouse: a big space measured in numbered floor-tiles, with no shelving, no aisles, no filing system — just addressable square footage. You could pile things on the floor by tile number, but nobody works that way. The shelving and the index (partitions and a filesystem) come next.

Where the analogy stops working. A warehouse floor is visible and physical; a block device is an abstraction the kernel presents, and the "warehouse" might be a physical SSD, a slice of a SAN, a network volume, or a file pretending to be a disk — Linux treats them identically. That uniformity is powerful (the same tools work on all of them) and occasionally a trap: the /dev/vda "disk" on your cloud VM is really a slice of some storage array three racks away, with performance nothing like a local SSD.

🧪 Exercise A1.1 — See the block devices (read-only, safe anywhere)
bash
lsblk                    # the tree of disks, partitions, and where they're mounted
lsblk -f                 # add filesystem type, label, and usage
✅ Expected result — click to reveal
javascript
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
vda    254:0    0   256G  0 disk
├─vda1 254:1    0   255G  0 part /
└─vda2 254:2    0     1G  0 part [SWAP]

(on a cloud VM; a laptop shows sda/nvme0n1, and yours will differ)

What to read out of it:

  • TYPE disk is the whole device; TYPE part are partitions within it, shown as a tree (├─). Here one 256G disk is split into a 255G root partition and a 1G swap partition — the layout Part A explains bottom-up.
  • MOUNTPOINTS ties it back to Module 2's tree: vda1 is mounted at /, so the root of everything you've been using lives on that partition. [SWAP] marks swap (Part D).
  • SIZE and RO (read-only) at a glance; lsblk -f adds the filesystem type (ext4, xfs) and how full each is. lsblk is the first command of any storage investigation — it shows the physical reality beneath the tidy filesystem you normally see.

A2. Partitions — dividing a disk

Official docs: lsblk(8) · mount(8)

🧠 A partition is a contiguous slice of a block device, treated as its own smaller block device. Why divide a disk: to separate concerns (keep /var/log from filling /), to run different filesystems on one disk, or to isolate swap. The partition table — the map of slices — comes in two formats: MBR (old, max 2TB, ≤4 primary partitions) and GPT (modern, huge disks, many partitions — the default now). Partitioning tools are fdisk, parted, and friends. Crucially, a partition is still raw space — slicing the warehouse into rooms doesn't install shelving; that's the filesystem, next.

Trap — partitioning is destructive and unforgiving. Writing a partition table rewrites the disk's map; get the device name wrong (/dev/sda when you meant /dev/sdb) and you've discarded a disk's worth of data with no undo and no confirmation beyond the one the tool gives. This is the command where "measure twice, cut once" is literal. Always lsblk first to confirm which device is which (by size, by mountpoint), never partition a mounted/in-use disk, and on production, partitioning is a change-managed operation, not something typed from memory. Every experienced engineer has one story here; the goal is to not be the story.
Real-world analogy — dividing the warehouse into rooms

Partitioning erects internal walls in the empty warehouse: this room for shipping, that room for cold storage, a small room for overflow. Each room is now a self-contained space you can fit out independently. The partition table is the floor plan pinned by the door.

Where the analogy stops working. Rebuilding a warehouse wall is disruptive but recoverable — the stuff inside can be moved first. Rewriting a partition table doesn't "move the stuff"; it rewrites the floor plan while pretending the rooms are empty, and the filesystem inside a repartitioned slice is simply orphaned. There is no "the movers will handle it" — you evacuate (back up) first, or you lose it.

🧪 Exercise A2.1 — Read the partition layout (read-only, safe anywhere)
bash
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS    # choose exactly the columns you want
cat /proc/partitions | head                    # the kernel's raw partition list
✅ Expected result — click to reveal
javascript
NAME   SIZE TYPE FSTYPE MOUNTPOINTS
vda    256G disk
├─vda1 255G part ext4   /
└─vda2   1G part swap   [SWAP]

What to read out of it:

  • The FSTYPE column is the layer A3 adds: vda1 isn't just a 255G slice, it holds an ext4 filesystem; vda2 holds swap. A partition with an empty FSTYPE is raw — sliced but not yet formatted, useless for files until you put a filesystem on it.
  • /proc/partitions is the kernel's own view (Module 2's /proc) — the ground truth lsblk prettifies. Reading storage from /proc and /sys is how monitoring tools get their numbers without shelling out.
  • lsblk -o (custom columns) is the habit worth keeping — like ps -o (Module 8), you ask for exactly the fields the question needs.

A3. Filesystems — turning raw space into files

Official docs: mount(8) · inode(7)

🧠 A filesystem is the organizing structure written onto a partition that turns numbered blocks into named files and directories — the layer that makes /etc/hostname meaningful instead of "block 4,192,006". Creating one is formatting: mkfs.ext4 /dev/sdb1 writes an empty ext4 filesystem onto that partition. The common types: ext4 (the reliable Linux default), xfs (great for large files, Red Hat's default), btrfs/zfs (advanced — snapshots, checksums), vfat/exfat (for USB sticks and cross-OS compatibility), tmpfs (a filesystem in RAM, not on disk — that's what /dev/shm and much of /run are). Every filesystem carries the inode table you met in Module 3 — which is why a filesystem can run out of inodes independently of running out of space (Part D's counter-intuitive incident).

Real-world analogy — fitting out the empty room

The filesystem is the shelving, aisle signs, and card catalogue installed in the empty room: now things have addresses ("aisle 3, shelf B"), there's an index to find them, and a rule for where new items go. mkfs is the fit-out crew — and formatting an occupied room means throwing out everything first (formatting erases).

Where the analogy stops working. A shelving system is one fixed design. Filesystems are plural and chosen — ext4 vs xfs vs btrfs are genuinely different fit-outs with different strengths (xfs for a few enormous items, btrfs for snapshotting the whole room), and picking the wrong one for the workload is a real performance and operations decision, not a cosmetic one. The room looks the same from the aisle; the machinery behind the shelves differs a lot.

🧪 Exercise A3.1 — Identify your filesystems (read-only, safe anywhere)
bash
df -T                    # -T adds the filesystem TYPE column
findmnt --real           # a clean tree of only the "real" mounts (skips the virtual ones)
✅ Expected result — click to reveal
javascript
Filesystem     Type  1K-blocks     Used Available Use% Mounted on
/dev/vda1      ext4  263174148 12093104 237656916   5% /
tmpfs          tmpfs   4108096        0   4108096   0% /dev/shm

then findmnt as a tree of TARGET/SOURCE/FSTYPE/OPTIONS.

What to read out of it:

  • / is ext4 on a real partition; /dev/shm is tmpfs — a filesystem living in RAM, which is why it's blazing fast and why anything there vanishes on reboot. Not everything that looks like a disk is a disk.
  • findmnt draws mounts as the tree they actually form (Part C), far more readable than raw mount output — the modern tool for "what's mounted where, and how?"
  • You've now seen all three layers in one view: the device (/dev/vda1), its filesystem (ext4), and where it's attached in the tree (/). Everything else in this module operates on this stack.

Part A — Interview questions

🎯 "What is a File system in Linux? Explain different file system types in Linux." — asked verbatim in Turing's 100+ Linux interview questions (2025)

A filesystem is the on-disk structure that organizes a block device's raw storage into named files and directories, with metadata (inodes — owner, permissions, timestamps, block locations) and a directory hierarchy. Common Linux types: ext4 (the general-purpose default — reliable, journaled), xfs (high-performance for large files/parallel I/O, Red Hat's default), btrfs/zfs (copy-on-write with snapshots and checksums), vfat/exfat (cross-platform removable media), plus in-memory tmpfs and the virtual proc/sysfs that aren't storage at all. You create one with mkfs.type, identify it with df -T/lsblk -f, and it sits on a partition of a block device.

The details that separate candidates: the three-layer stack (block device → partition → filesystem) rather than treating "disk" and "filesystem" as one thing; a real reason to pick xfs over ext4 (huge files, high parallelism) instead of listing types; and that /proc and /sys are virtual filesystems (kernel data dressed as files, Module 2), which shows you understand a filesystem is an interface, not necessarily storage.

🎯 "How would you create an ext4 file system?" — asked verbatim in Turing's 100+ Linux interview questions (2025)

sudo mkfs.ext4 /dev/sdb1 — format the target partition (not the whole disk unless you mean to) with an empty ext4 filesystem, optionally -L label to name it. Then create a mount point (sudo mkdir /mnt/data), mount it (sudo mount /dev/sdb1 /mnt/data), and add an /etc/fstab entry (Part C) so it mounts at boot. Confirm with lsblk -f and df -T.

The details that separate candidates: the emphatic warning that mkfs erases the target — so you confirm the device with lsblk first and never format a mounted/in-use partition; formatting the partition not the raw disk; and the full workflow (format → mkdir → mount → fstab → verify), because "create a filesystem" in the real world means "make it usable and persistent", not just the one mkfs command.

Part B — Measuring space, and the full-disk incident

B1. df — how full is each filesystem

🧠 df (disk free) answers "how full is each filesystem?" — one line per mounted filesystem, with size, used, available, and use%. Always run it as df -h (human-readable: G/M instead of raw 1K-blocks). It reads the filesystem's own accounting, so it's instant regardless of how many files exist — it does not walk the tree. This is the first command of every disk-full incident: it tells you which filesystem is full, which is not always the obvious one (a full /var and an empty /home are separate filesystems that df shows separately).

Real-world analogy — the fuel gauge

df is the fuel gauge: one glance tells you how full each tank is, instantly, without measuring the fuel. A car with several tanks (several filesystems) has a gauge per tank — and "the car is out of fuel" always means one specific tank, which the gauges tell you at once.

Where the analogy stops working. A fuel gauge reads the actual liquid. df reads the filesystem's bookkeeping, and bookkeeping can lie in one specific, famous way: a file that's been deleted but is still held open by a process still occupies space the filesystem counts as used — so df shows the tank full while du (which walks visible files) shows it should be empty. That df-says-full-but-du-says-empty gap is Part B's signature incident, and it's not a bug — it's the fuel still in a hidden reserve nobody can see by looking at the visible tank.

🧪 Exercise B1.1 — Read the gauges (read-only, safe anywhere)
bash
df -h                    # every filesystem, human-readable
df -h /                  # just the one holding the root
df -h /var               # which filesystem is /var on? (may be / or its own)
✅ Expected result — click to reveal
javascript
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           7.9G     0  7.9G   0% /dev/shm
/dev/vda1       252G   12G  237G   5% /
tmpfs           4.0G     0  4.0G   0% /run

What to read out of it (your sizes and device names differ):

  • Use% is the number monitoring alerts on, and the one to read first in an incident — a filesystem at 100% is the problem; the others are noise. Avail is what you actually have left (note it can be less than Size−Used, because filesystems reserve ~5% for root — Part D).
  • Mounted on maps each filesystem to its place in the tree. If df -h /var shows the same device as df -h /, then /var lives on the root filesystem — so a log flood in /var/log fills / and can take down the whole machine. If it shows a different device, /var is isolated and a log flood only fills that. Which one your machine is decides how a log flood plays out — check before you need to know.
  • The tmpfs lines are RAM filesystems (A3) — they "use disk" of zero because they're memory; filling one is a memory problem, not a disk one.

B2. du — what's using the space

🧠 df says which filesystem is full; du (disk usage) says what in it is using the space, by walking directories and summing file sizes. The incident-response idiom, worth memorizing exactly: du -h /var | sort -rh | head — human sizes, sorted largest-first, top few. More surgically, du -sh /var/* gives one summary line per item in /var, and you drill into the biggest (du -sh /var/log/*, then deeper) until you find the culprit. Because du walks the tree, it's slow on huge directories — but it's the tool that turns "the disk is full" into "this 40GB log file is the problem".

Counter-intuitive: df and du routinely disagree, and both are right. df counts what the filesystem says is used; du counts what visible files add up to. They diverge for real reasons: (1) a deleted-but-open file — its name is gone so du can't see it, but its blocks are still allocated so df counts them (the classic "deleted the huge log, disk didn't shrink" — Module 3's inode lesson, now an incident); (2) files under a mount point — if something is mounted over /mnt/data, du / can't see files hidden beneath the mount, but df counts the underlying filesystem; (3) filesystem reserved blocks and overhead. When df says full and du says there's room, suspect a deleted-open file first — and the fix isn't rm (there's no name to remove), it's finding the process holding it (lsof) and restarting it, or truncating the file through /proc.
Real-world analogy — the itemized audit versus the gauge

If df is the fuel gauge, du is sending an auditor to physically weigh every box in the warehouse and total it up. The audit tells you what's heavy — invaluable for "what do I delete?" — but it takes time proportional to the number of boxes, and it can only weigh boxes it can see.

Where the analogy stops working. The auditor counts visible boxes; the gauge measures total load including a crate that fell behind the shelving (deleted-but-open file) that the auditor walks right past. When the gauge and the audit disagree, the discrepancy is the clue — something is consuming space that has no visible name, and chasing that gap is exactly how you solve the incidents that defeat people who only know du.

🧪 Exercise B2.1 — Hunt the space (read-only, safe anywhere)
bash
du -sh /usr/share                    # total size of one directory
du -h /var 2>/dev/null | sort -rh | head -5     # biggest things under /var
du -sh /var/* 2>/dev/null | sort -rh | head -6  # one line per item in /var, biggest first
✅ Expected result — click to reveal
javascript
1.1G	/usr/share

then, for /var:

javascript
236M	/var/lib
5.7M	/var/cache
1.4M	/var/log
20K	/var/spool
4.0K	/var/tmp

What to read out of it (your sizes vary hugely by machine):

  • du -sh /var/* is the incident-response drill: one number per subdirectory, sorted, so the culprit is line one. Here /var/lib dominates (package and app data) — on a machine with a real problem, you'd see a /var/log at 40G and know instantly where to look.
  • The 2>/dev/null (Module 5) hides permission-denied noise on directories you can't fully read — the same wide-search etiquette as Module 6's find. The totals stay honest for what you can see; run with sudo for the complete picture.
  • Drill down by repeating on the winner: du -sh /var/lib/*, then into its biggest, until you reach the specific file or directory. Three or four du -sh .../* | sort -rh | head steps find almost any space hog.
Now imagine this at 500 hosts. You cannot du your way across 500 machines during an incident. The fleet version: monitoring already tracks df per filesystem on every host and alerts before 100% (at 80–85%), so you're paged with time to act, not after the outage. When paged, you know the host and filesystem from the alert, SSH in, and run the du drill on that one box. And the prevention is upstream: log rotation (so /var/log self-limits — the reason logs don't grow forever on a healthy system), separate filesystems for volatile data (so a flood can't fill /), and quotas. "The disk filled up" on a well-run fleet is a monitoring gap or a rotation failure, not a surprise.

B3. The full-disk incident, end to end

Official docs: df(1) · du(1) · lsof(8)

🧠 Put it together into the procedure you'll run for real. 1. df -h — which filesystem is full? 2. du -sh /that/fs/* | sort -rh | head, drilling down — what's using it? 3a. If du finds a big file you can delete or rotate — do it (but see the deleted-open trap). 3b. If df says full but du can't account for it — a deleted-but-open file: find the culprit with sudo lsof | grep deleted, and restart the process holding it (or truncate via /proc/PID/fd/), because there's no filename to rm. 4. Get breathing room fast if needed: apt clean (Module 9's package cache), truncate/rotate the offending log, clear /tmp. 5. Fix the root cause: rotation, a separate filesystem, or a quota — so it doesn't recur tonight.

Real-world analogy — the flooding-basement callout

A full disk is a flooding basement: first find which basement is flooding (df), then find the source — the burst pipe, not just the water (du drilling down). Sometimes the water has an obvious source you shut off (delete the big file); sometimes the water keeps rising with no visible source (deleted-open file — the pipe is behind the wall, and mopping the visible floor does nothing).

Where the analogy stops working. A plumber can always eventually see the pipe. The deleted-open file is genuinely invisible to the file tools — ls, du, find all show nothing, because the name is gone while the data lives on in a process's grip. You can only reach it sideways, through the process holding it (lsof, /proc/PID/fd). It's the one storage problem where the obvious tools are blind by design, which is exactly why it defeats people and why it's a favourite interview question.

🧪 Exercise B3.1 — Reproduce the deleted-open-file mystery (safe — uses a small temp file in /tmp)
bash
cd /tmp
# Create a 50MB file, hold it open in a background process, then DELETE its name:
python3 -c "
import time
f = open('/tmp/ghost.bin','wb'); f.write(b'x'*(50*1024*1024)); f.flush()
import os; os.remove('/tmp/ghost.bin')   # name gone
print('name deleted; now sleeping while holding the file open')
time.sleep(30)
" &
sleep 1
ls -la /tmp/ghost.bin 2>&1               # the name is GONE — du/ls can't see it
sudo lsof 2>/dev/null | grep -i ghost | head -2   # but a process still holds it (space still used)
wait                                     # let the python finish; space frees when it closes the file
✅ Expected result — click to reveal
javascript
name deleted; now sleeping while holding the file open
ls: cannot access '/tmp/ghost.bin': No such file or directory
python3   40231  root  3w  REG  254,0  52428800  ...  /tmp/ghost.bin (deleted)

What to read out of it:

  • ls (and du, and find) report the file gone — the name is deleted. Yet 50MB is still allocated, because the python process holds the file open (Module 3: a file survives until its link count is zero and no process holds it open).
  • lsof reveals the truth: the process, its open file descriptor (3w), the 52428800 bytes, and the tell-tale (deleted) marker. This is exactly what you'd see on a real box where df says full but du says empty — scaled up to a 40GB deleted log some service is still writing to.
  • The fix on a real incident is not rm (no name to remove) — it's restart the process (systemctl restart theservice, Module 11), which closes the descriptor and frees the space instantly; or, to avoid downtime, truncate it in place via > /proc/40231/fd/3. Here, wait lets python exit and the space frees on its own.

Part B — Interview questions

🎯 "How do you check disk usage?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026); Adaface (2024) asks "What is the command to check disk space usage?" and its per-directory variant

Two complementary tools: df -h for filesystem-level free space (which filesystem is how full — the fuel gauge), and du -sh path / du -h path | sort -rh | head for what's consuming space within a directory (the itemized audit). df reads filesystem accounting instantly; du walks the tree, so it's slower but tells you what to delete. lsblk/df -T add the device and filesystem-type view.

The details that separate candidates: using them together as a drill (df to localize, du to hunt) rather than naming one; the df/du divergence and the deleted-open-file cause (the standout answer); the du -sh /* drill-down technique; and mentioning that at scale you don't run these live — monitoring alerts on df before 100%, and you run du on the one flagged host.

🎯 "How do you find the top 10 largest files and directories on your Linux system?" / "Explain how to find the largest files in a directory tree, sorted by size." — asked verbatim in Turing's set (2025) and Adaface's (2024) respectively

Largest directories: du -h /path | sort -rh | head -10. Largest files specifically: find /path -type f -printf '%s\t%p\n' 2>/dev/null | sort -rn | head -10 (or find /path -type f -exec du -h {} + | sort -rh | head — Module 6's find + Module 7's sort composed). For a live incident, du -sh /path/* | sort -rh | head and drill into the winner.

The details that separate candidates: distinguishing largest files (find) from largest directories (du) — they're different questions; the composition of find + du + sort from earlier modules; sort -rh (human-numeric reverse, so 2G sorts above 900M — Module 7's exact trap); and scoping the search to the relevant filesystem rather than / to avoid crossing into other mounts (-xdev).

Part C — Mounting: attaching storage to the tree

C1. mount and umount — grafting a filesystem in

🧠 Module 2 taught that Linux has one tree and no drive letters — a second disk is grafted into the tree at a directory. That grafting is mounting. sudo mount /dev/sdb1 /mnt/data attaches the filesystem on /dev/sdb1 at the directory /mnt/data — now reading /mnt/data/file reads from that disk. The directory you mount onto is the mount point (an ordinary directory; anything already in it is hidden while the mount is active — B2's du gotcha). sudo umount /mnt/data detaches it. mount with no arguments (or findmnt) lists everything currently mounted. This is the mechanism behind every disk, USB stick, and network share appearing in the tree.

Trap — you can't unmount a filesystem that's in use. umount fails with target is busy if any process has a file open on it or any shell is cd'd into it — including your own shell sitting in /mnt/data. The fix is to find and stop the users: cd out first, then sudo lsof /mnt/data or sudo fuser -m /mnt/data shows what's holding it, and you stop those processes (or use umount -l, a lazy unmount, with care). Force-unmounting a busy filesystem with data mid-write can corrupt it — "busy" is a safety feature, not an obstacle. This is also why you umount a USB stick before pulling it: detaching flushes pending writes that are still in memory.
Real-world analogy — connecting a building to the campus

A new building (a filesystem) is finished, but nobody can reach it until it's connected to the campus walkway system at a specific door (the mount point). Mounting builds that connection; now the building is part of the campus and people walk to it by the normal paths. Unmounting seals the connection — and you can't seal a doorway people are currently walking through.

Where the analogy stops working. A sealed-off building still physically exists and its contents are safe. An unmounted filesystem's data is intact too — but a filesystem unmounted uncleanly (power pulled, force-unmounted mid-write) can be left inconsistent, because writes buffered in memory never reached the disk. The campus walkway doesn't lose the building's contents when disconnected roughly; a filesystem can lose recent writes, which is why clean unmount (and sync) matters and why "just pull the USB" corrupts sticks.

🧪 Exercise C1.1 — Read the mount table (read-only, safe anywhere)
bash
findmnt --real                       # the real (non-virtual) mounts as a tree
mount | grep -w /                    # how is the root filesystem mounted?
cat /proc/mounts | grep -v tmpfs | head    # the kernel's raw mount list
✅ Expected result — click to reveal
javascript
TARGET SOURCE    FSTYPE OPTIONS
/      /dev/vda1 ext4   rw,relatime

then the root's mount line, e.g.:

javascript
/dev/vda1 on / type ext4 (rw,relatime)

What to read out of it:

  • Each mount is SOURCE (the device) → TARGET (where in the tree) → FSTYPE → OPTIONS. The root / is /dev/vda1, ext4, mounted rw (read-write). Everything you've done all track has happened on this one mount.
  • The OPTIONS matter operationally: ro (read-only), noexec (no running programs from here — a security hardening for /tmp), nosuid, relatime (a performance choice about atime updates — Module 3's timestamps). Mount options are a real security and performance lever, set per-filesystem in fstab (next).
  • /proc/mounts is the kernel's authoritative list; findmnt and mount are friendlier views of the same truth.

C2. /etc/fstab — mounts that survive reboot

🧠 A mount command mounts now but is forgotten on reboot — the machine comes up without it. /etc/fstab (filesystem table) is the file listing filesystems to mount automatically at boot: systemd reads it and mounts everything in it. Each line has six fields: device (best identified by UUID= — see the trap), mount point, filesystem type, options (defaults, ro, noexec…), dump (backup flag, usually 0), and fsck order (boot-time check order — 0 skip, 1 for root, 2 for others). "Permanently mount a filesystem" — a verbatim interview question — means: add its fstab line.

Trap — a bad fstab entry can stop the machine from booting. fstab is read at boot, and a broken entry (wrong device, wrong options, a device that isn't present) can drop the boot into emergency mode instead of coming up — a genuinely serious outage, especially on a remote server you can't walk over to. Two disciplines prevent it: (1) reference filesystems by UUID= (from blkid or lsblk -f), never by /dev/sdb1 — device names can change between boots when disks are added or reordered, and yesterday's sdb might be today's sdc, mounting the wrong thing or nothing; (2) test before rebooting — after editing fstab, run sudo mount -a (mount everything in fstab now) and confirm no errors, so you find a mistake while the machine is still up, not on the reboot. The nofail option is also worth knowing: it lets boot continue even if that filesystem is missing (right for removable or network mounts).
Real-world analogy — the standing instructions versus a one-off request

A live mount command is asking the doorman to open a connection right now — helpful today, forgotten tomorrow. An fstab entry is the standing instruction in the building's opening procedure: "every morning, connect these buildings to the walkway". Written once, followed at every start-up.

Where the analogy stops working. A doorman who finds a standing instruction impossible ("that building doesn't exist any more") shrugs and moves on. Boot-time fstab is far less forgiving: a mount it can't satisfy can halt the whole opening procedure (drop to emergency mode) rather than skip the one building — unless you explicitly wrote nofail. The standing instruction has veto power over the entire startup, which is why a careless edit is dangerous and mount -a testing is non-negotiable.

🧪 Exercise C2.1 — Read your fstab and match it to reality (read-only, safe anywhere)
bash
cat /etc/fstab                       # the standing mount instructions
lsblk -f                             # the UUIDs to reference (compare against fstab)
# The safe test after any edit (harmless to run now — mounts anything not yet mounted):
sudo mount -a && echo "fstab is consistent — no errors"
✅ Expected result — click to reveal
javascript
# <file system>   <mount point>   <type>  <options>       <dump>  <pass>
UUID=a1b2c3d4-...  /              ext4    defaults        0       1
UUID=e5f6...       none           swap    sw              0       0

(a minimal cloud image may have very few lines; yours will differ)

What to read out of it:

  • The root filesystem is referenced by UUID=, not /dev/vda1 — the robust identification the trap insists on. Compare the UUID against lsblk -f's output to see it's the same filesystem, named two ways.
  • Six fields per line, in order: device, mount point, type, options, dump, pass. The pass field (1 for root, 2 for others, 0 to skip) controls boot-time filesystem checking order — root gets checked first.
  • sudo mount -a is the safety check: it attempts every fstab mount that isn't already mounted and errors loudly if a line is broken — run it after every fstab edit, before you ever reboot. On a healthy system it's silent (Module 1: silence is success). This one habit prevents the unbootable-server incident.

Part C — Interview questions

🎯 "How do you permanently mount a filesystem?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)

Add an entry to /etc/fstab: device (as UUID=…), mount point, filesystem type, options, dump flag, and fsck pass — then sudo mount -a to apply and test it without rebooting. A live mount /dev/sdb1 /mnt/data mounts only until reboot; fstab is what makes it survive, because systemd reads fstab at every boot and mounts everything listed.

The details that separate candidates: the temporary-vs-permanent distinction (mount now vs fstab forever); using UUID over device names (which can change) and why; testing with mount -a before rebooting to avoid an unbootable machine; and knowing nofail for removable/network mounts so a missing one doesn't halt boot. This question is a magnet for the "I edited fstab and the server wouldn't boot" war story — leading with the UUID + mount -a safeguards signals you've been burned and learned.

🎯 "How would you configure a Linux system to automatically mount a network share on boot?" / "How can you mount a partition with the NTFS file system type in read-write mode?" — asked verbatim in Adaface (2024) and Turing (2025) respectively

Network share (NFS example): install the client (nfs-common), then an fstab line server:/export /mnt/share nfs defaults,_netdev,nofail 0 0 — _netdev waits for the network before mounting (crucial: a network mount attempted before networking is up fails), nofail so a missing server doesn't block boot. NTFS read-write: install ntfs-3g, then sudo mount -t ntfs-3g /dev/sdb1 /mnt/win (or fstab with type ntfs-3g) — the ntfs-3g driver provides safe read-write NTFS that the older kernel driver didn't.

The details that separate candidates: _netdev/nofail for network mounts (the two options that prevent boot hangs on a dependency that may be slow or absent — the systemd ordering lesson from Module 11, applied to storage); knowing NTFS read-write needs the ntfs-3g driver historically; and that CIFS/SMB (Windows shares) vs NFS (Unix) are different filesystem types with different fstab syntax — picking the right one for the share.

Part D — The layers on top: LVM, swap, inodes

D1. LVM — storage you can resize

🧠 A raw partition has a fixed size — outgrow it and you're repartitioning, which is destructive (Part A). LVM (Logical Volume Manager) inserts a flexible layer between physical disks and filesystems so you can resize, combine, and snapshot storage without repartitioning. Its three concepts stack: physical volumes (PV) — whole disks or partitions handed to LVM (pvcreate /dev/sdb); volume groups (VG) — a pool combining one or more PVs into one big allocatable space (vgcreate data /dev/sdb /dev/sdc); logical volumes (LV) — the "virtual partitions" you carve from a VG and put filesystems on (lvcreate -L 50G -n web data). The payoff: a filesystem running low can be grown live — add a disk to the VG, extend the LV (lvextend), grow the filesystem (resize2fs) — no downtime, no repartition. This is why servers and cloud VMs almost always use LVM.

Real-world analogy — the flexible-wall office floor

Fixed partitions are permanent drywall — an office is the size it's built, and enlarging it means demolition. LVM is a modern floor with movable partition walls on tracks: the whole floor is a pool of space (the VG), and you draw rooms (LVs) of whatever size, resizing them by sliding walls as needs change — and you can annex the floor next door (add a PV) to make the pool bigger.

Where the analogy stops working. Movable office walls still divide a fixed total floor area. LVM can grow the total by annexing more disks into the VG, so the pool itself expands — there's no fixed building footprint. And LVM snapshots have no physical-wall equivalent at all: a frozen point-in-time copy of a volume you can back up or roll back to, taken instantly. The flexibility is genuinely beyond what rearranging physical space can do.

🧪 Exercise D1.1 — Inspect LVM if present (read-only, safe; may show nothing on a non-LVM machine)
bash
lsblk                                # LVM volumes appear as TYPE lvm under their disks
sudo pvs 2>/dev/null || echo "no LVM physical volumes (or lvm2 not installed)"
sudo vgs 2>/dev/null; sudo lvs 2>/dev/null   # volume groups and logical volumes
✅ Expected result — click to reveal

On an LVM machine:

javascript
PV         VG        Fmt  Attr PSize   PFree
/dev/sda3  ubuntu-vg lvm2 a--  <62.00g    0
VG        #PV #LV #SN Attr   VSize   VFree
ubuntu-vg   1   1   0 wz--n- <62.00g    0
LV        VG        Attr       LSize
ubuntu-lv ubuntu-vg -wi-ao---- <62.00g

On a machine without LVM (many minimal cloud images), the pvs line prints the "no LVM" message — that's a valid result too.

What to read out of it:

  • pvs/vgs/lvs are the three-level view: physical volumes fed in, grouped into a VG pool, carved into LVs. In lsblk, an LV shows as TYPE lvm nested under the partition that's its PV — the flexible layer made visible.
  • VFree on the VG is the headroom: space in the pool not yet assigned to any LV, available to extend an LV live. VFree 0 means the pool is fully allocated — to grow anything you'd first add a disk (pvcreate + vgextend).
  • Ubuntu's guided installer names things ubuntu-vg/ubuntu-lv by default — recognizing that layout tells you a machine is LVM-managed and can be grown without downtime, which changes how you respond to a filling disk.

D2. Swap — disk as overflow memory

Official docs: swapon(8) · free(1)

🧠 Swap is disk space used as overflow when physical RAM fills: the kernel moves less-used memory pages out to a swap partition or swap file, freeing RAM for active work. It's a safety valve, not a speed feature — disk is thousands of times slower than RAM, so a system swapping heavily ("thrashing") is crawling. free -h shows RAM and swap usage; swapon --show lists active swap. Swap lets a box survive a memory spike instead of instantly OOM-killing (Module 8's kernel killer, Module 14's subject); but sustained swap use means you need more RAM, not more swap. Modern systems often use a swap file (/swapfile) rather than a partition — easier to resize.

Counter-intuitive: high swap usage is not automatically bad, and low "free" memory is normal. Two memory illusions bite beginners. First, free shows most RAM as "used" even on an idle system — because Linux uses spare RAM as disk cache (the buff/cache column), which is available to programs the instant they need it, not truly consumed. The number that matters is available, not free — a machine with 200MB free but 6GB available has plenty. Second, some pages sitting in swap is fine if they're genuinely idle (why keep a never-touched page in precious RAM?); what's bad is active swapping — pages constantly moving in and out (si/so in vmstat, Module 14). "Free memory is low" and "swap is nonzero" both look alarming and usually aren't; the real signals are available memory near zero and sustained swap traffic.
Real-world analogy — the desk and the filing cabinet

RAM is your desk — fast to reach, limited surface. Swap is the filing cabinet across the room: far more room, far slower to fetch from. When the desk fills, you move rarely-needed papers to the cabinet (swap out) to keep working space clear. Occasional trips to the cabinet are fine; constantly shuttling papers back and forth because everything is needed at once is thrashing — you spend all your time walking, none working.

Where the analogy stops working. A desk that's "full" of papers is obviously full. RAM that looks full is usually mostly cache — papers you left out because there was room, instantly clearable when you need the space, not actually cluttering your work. The desk metaphor makes low "free" RAM look like a problem; in Linux it's the system using spare RAM productively as cache, and empty RAM would actually be waste. That inversion — full-looking memory is healthy, the available column is the truth — is the single most misread thing in Linux operations.

🧪 Exercise D2.1 — Read memory and swap honestly (read-only, safe anywhere)
bash
free -h                              # RAM and swap, human-readable
swapon --show                        # active swap devices/files (may be empty)
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|SwapTotal|SwapFree'  # the raw truth
✅ Expected result — click to reveal
javascript
               total        used        free      shared  buff/cache   available
Mem:           7.8Gi       684Mi       6.8Gi        12Mi       644Mi       7.2Gi
Swap:             0B          0B          0B

(this machine has no swap configured — common on containers and some cloud images)

What to read out of it:

  • Read the columns in the right order: available (7.2Gi here) is what programs can actually get — not free. buff/cache (644Mi) is RAM used as disk cache, counted as "used" but reclaimable — which is why used + free doesn't tell the real story and available does.
  • Swap: 0B — this machine has none, so a memory spike here goes straight to the OOM killer with no cushion (Module 8/14). A production box usually has some swap as a shock absorber.
  • /proc/meminfo is the kernel's raw memory accounting (Module 2's /proc); free and every memory monitor read from it. When you want the exact numbers behind a monitoring alert, this is the source.

D3. Inodes — running out of files, not space

🧠 Module 3 taught that every file needs an inode (its metadata record), and that a filesystem has a fixed number of them, set at format time. The consequence lands here: a filesystem can run out of inodes while it has plenty of space — millions of tiny files (a runaway mail queue, a session-file leak, cache gone wild) exhaust the inode table, and then new files fail with "No space left on device" even though df -h shows the disk half empty. It's the most confusing storage error there is, because the message says "no space" and df seems to disagree. The tell is df -i (inode usage) — IUse% 100% with Use% 45% is the fingerprint. The fix: find and delete the swarm of tiny files (du-drill, then a careful find … -delete), because an ext4 filesystem's inode density (inodes per byte) is set at format time and can't be raised in place — growing the filesystem adds some inodes proportionally, but you can't simply mint more into the existing size.

Counter-intuitive: "No space left on device" can mean you have plenty of space. The error is about inodes, not bytes, and the two are counted separately. A filesystem is "full" when it runs out of either — bytes (big files) or inodes (too many files, however small). df -h shows only the byte gauge; a filesystem at 45% bytes but 100% inodes throws the exact same "No space left on device" as one at 100% bytes, and the beginner stares at df -h's free space in disbelief. Always check both gauges — df -h and df -i — when a disk-full error doesn't match the space you can see. This one distinction resolves an incident that can otherwise burn an afternoon.
Real-world analogy — the library that ran out of catalogue cards

A library has shelf space (bytes) and a card catalogue with a fixed number of blank cards (inodes). Fill the shelves and you're out of room the obvious way. But run out of catalogue cards — because someone shelved a million pamphlets, one card each — and you can't add a single new book even with empty shelves, because there's no card left to index it. "The library is full" is true, and pointing at the empty shelves doesn't help: the constraint is the catalogue, not the space.

Where the analogy stops working. A librarian can order more blank cards. An ext4 filesystem's inode density is fixed at format time — you can't raise inodes-per-byte in place. Growing the filesystem (LVM + resize2fs, D1) adds inodes proportional to the new space, but at the same density; to get more inodes for the same size you must delete files, reformat with a higher inode count, or move to a filesystem like xfs that allocates inodes dynamically. The catalogue's density can't be increased in place, which makes chronic inode exhaustion a delete-files, grow-the-volume, or reformat problem — not a quick in-place top-up.

🧪 Exercise D3.1 — Check the inode gauge (read-only, safe anywhere)
bash
df -h /                              # the BYTE gauge
df -i /                              # the INODE gauge — the one people forget
# find which directory holds the most files (inode consumers), not the most bytes:
sudo find /var -xdev -type f 2>/dev/null | cut -d/ -f1-3 | sort | uniq -c | sort -rn | head
✅ Expected result — click to reveal
javascript
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1       252G   12G  237G   5% /
Filesystem       Inodes  IUsed    IFree IUse% Mounted on
/dev/vda1      16777216 276190 16501026    2% /

then a count of files per top-level /var subdirectory, biggest first.

What to read out of it:

  • Two independent gauges: 5% bytes used, 2% inodes used — a healthy machine has headroom on both. An inode-exhausted machine shows the second line at IUse% 100% while the first looks fine, and that mismatch is the whole diagnosis.
  • The find | cut | sort | uniq -c | sort -rn pipeline (Modules 6–7, composed) counts files per directory — the inode-hunting equivalent of du's byte-hunting. When inodes are the problem, you hunt directories with the most files, not the most bytes; a directory with a million 1KB files is invisible to du -sh (1GB, unremarkable) but is the entire inode problem.
  • -xdev keeps find on the one filesystem (Module 6's etiquette) — essential here, because you're diagnosing this filesystem's inode table, not wandering into others.

Part D — Interview questions

🎯 "Why is LVM required?" / "If a volume group VG0 already exists and we need to extend it up to 4 GB, how do we do it?" — both asked verbatim in Turing's 100+ Linux interview questions (2025)

LVM is required for flexible storage: resizing filesystems live (grow a volume running low without downtime or repartitioning), pooling multiple disks into one allocatable space, and snapshots for consistent backups — none of which fixed partitions allow. To extend VG0: add a new physical volume (sudo pvcreate /dev/sdc) then sudo vgextend VG0 /dev/sdc; to then grow a logical volume into that new space, sudo lvextend -L +4G /dev/VG0/lvname followed by sudo resize2fs /dev/VG0/lvname (ext4) or xfs_growfs (xfs) to grow the filesystem into the enlarged volume.

The details that separate candidates: the PV→VG→LV three-layer model stated cleanly; the crucial two-step growth (extend the LV and then grow the filesystem — people forget the filesystem doesn't auto-grow into a bigger LV); doing it live with no unmount as LVM's headline benefit; and snapshots as the other reason LVM earns its complexity. Naming resize2fs/xfs_growfs as the filesystem-grow step is the tell of someone who's actually done it.

🎯 A synthesis the banks under-ask — "the disk is full but df shows free space, what's happening?"

Two prime suspects, both taught here. Inode exhaustion: df -h shows free bytes but df -i shows IUse% 100% — too many files, not too many bytes; find and delete the swarm of tiny files (you can't add inodes to a live ext4 filesystem). Deleted-but-open file: df counts space that du can't see because a process holds a deleted file open — sudo lsof | grep deleted finds it, and restarting the process (or truncating via /proc/PID/fd) frees the space, since there's no name to rm. A third: files hidden under a mount point.

The details that separate candidates: checking both df -h and df -i reflexively when the error doesn't match visible space; knowing the deleted-open-file fix is restart/truncate, not rm; and connecting both back to Module 3 (inodes are finite; a file lives until its link count is zero and it's not held open). This is the storage question that most cleanly separates people who memorized commands from people who understand the filesystem — which is exactly why interviewers ask it.

Part E — Toolkit

Official docs: Part E is reference material — every source it draws on is linked in the E3 documentation table below.

E1. Production practice — symptoms and fixes

SymptomWhat is really happeningWhat to runThe fix
"No space left on device", df -h shows free spaceInode exhaustion — out of inodes, not bytesdf -i (look for IUse% 100%)Find and delete the swarm of tiny files; long-term, more inodes / xfs
Deleted a huge file, df didn't shrinkA process still holds the deleted file opensudo lsof | grep deletedRestart the process, or truncate via > /proc/PID/fd/N — not rm
df says full but du of the same path finds far lessDeleted-open file, or files hidden under a mount pointlsof | grep deleted; findmnt to check for overlaid mountsFree the open file, or investigate what's mounted over the directory
umount fails: "target is busy"A process (maybe your shell) has a file open on itcd / out first; sudo lsof /mnt/x or fuser -m /mnt/xStop the holders, then unmount; umount -l (lazy) as a careful last resort
Server won't boot after an fstab editA broken fstab line dropped boot to emergency mode(from console/rescue) fix the line; had you run mount -a first…Use UUID=, add nofail for optional mounts, always mount -a before reboot
Filesystem full and growing, but it's LVMYou can grow it live instead of firefighting deletionsvgs (check VFree)lvextend -L +NG … then resize2fs/xfs_growfs — no downtime
Alarm: "memory almost full" but the app is fineLow free is normal — RAM used as reclaimable cachefree -h — read the available column, not freeNothing, unless available is near zero or swap is actively churning

E2. Capstone — four tickets

Work these like real tickets: read the ticket, write the commands and the explanation you would send, then open the worked answer. Everything needed was taught in this module.
🎫 Ticket 1 — "Root filesystem at 100%, site is down, fix it NOW"

Ticket text: "vm-web-2 is alerting / at 100% and the app is throwing write errors. Get us breathing room immediately, then find and fix the cause."

Worked answer — run the incident procedure (B3) in order. Localize: df -h confirms / is the full filesystem (not a separate /var). Breathing room, fast and safe: sudo apt clean (reclaims the package cache, Module 9 — instant, harmless), clear old logs if rotation failed (journalctl --vacuum-size=200M, Module 11), and check df -h recovered a few %. Hunt the cause: sudo du -sh /* 2>/dev/null | sort -rh | head, drill into the winner (/var → /var/log → the giant file). The deleted-open check — if du can't account for the space df shows used: sudo lsof | grep deleted for a service still writing a deleted log; restart it to free the space. Root cause: almost always log rotation that stopped working or was never configured — fix logrotate/journald limits so /var/log self-caps, and consider moving /var to its own filesystem so a future flood can't take down /. The reply names the immediate reclaim, the specific culprit, and the prevention — not just "I deleted some files".

🎫 Ticket 2 — "'No space left on device' but df says the disk is half empty"

Ticket text: "The mail server can't write new files — every write fails with 'No space left on device' — but df -h shows /var at 48%. Is the disk lying?"

Worked answer: the disk isn't lying — you're reading the wrong gauge. df -h measures bytes; the error is almost certainly inode exhaustion. Confirm: df -i /var → expect IUse% 100% alongside the 48% bytes. Cause: a swarm of tiny files exhausted the fixed inode table — on a mail server, classically a stuck queue (/var/spool/) with millions of undelivered messages, or a session/cache leak. Find the swarm by file count, not size: sudo find /var -xdev -type f 2>/dev/null | cut -d/ -f1-4 | sort | uniq -c | sort -rn | head points at the directory holding the millions. Fix: carefully delete the swarm (find /var/spool/stuck -type f -delete after confirming it's safe to purge), which frees inodes; df -i recovers. Prevention: fix whatever produces the file swarm (flush/limit the mail queue, fix the leaking app), and note you cannot add inodes to a live ext4 filesystem — so if this recurs by design, the real fix is reformatting with more inodes or moving to xfs (dynamic inodes). The one-line lesson: when "no space" contradicts df -h, run df -i.

🎫 Ticket 3 — "We deleted the 30GB log but the disk is still full"

Ticket text: "The app log hit 30GB and filled the disk. We rm'd it — ls confirms it's gone — but df still shows the disk full. What now?"

Worked answer: the file's name is gone but its data isn't freed, because the app process still has it open and is still writing to it (Module 3: a file lives until link count zero AND no open handle). du can't see it (no name), df still counts it (blocks still allocated) — the classic divergence. Confirm: sudo lsof | grep deleted (or sudo lsof +L1) shows the app process, its file descriptor, the ~30GB size, and the (deleted) marker. Two fixes: (a) restart the app (sudo systemctl restart theapp, Module 11) — closes the descriptor, frees 30GB instantly — the clean choice if a brief restart is acceptable; (b) truncate in place without downtime: sudo truncate -s 0 /proc/<pid>/fd/<n> (or > /proc/<pid>/fd/<n>) empties the file the process is still holding, freeing the space while the app keeps running. Prevention: this is why apps should log to the journal (Module 11) or use logrotate with copytruncate/signals — so rotation frees space correctly instead of leaving a giant deleted-but-open ghost. Explain that rm was the wrong tool because there was no name to remove — the space was never the filename's to free.

🎫 Ticket 4 — "Database volume is filling and we can't take downtime"

Ticket text: "The Postgres data volume (/dev/data/pgdata, an LVM volume) is at 88% and climbing. We cannot stop the database. Give us headroom without an outage."

Worked answer — this is exactly what LVM exists for (D1). Because it's a logical volume, you grow it live, no unmount, no downtime. Check the pool: sudo vgs — is there VFree in the volume group? If yes, skip to extend. If the VG is full, add capacity first: attach a new disk (cloud console or physical), sudo pvcreate /dev/sdX, sudo vgextend data /dev/sdX — the pool grows. Extend the volume: sudo lvextend -L +50G /dev/data/pgdata (or -l +100%FREE to take all remaining pool space). Grow the filesystem into the bigger volume — the step people forget: sudo resize2fs /dev/data/pgdata (ext4) or sudo xfs_growfs /pgdata-mountpoint (xfs). df -h now shows the headroom, Postgres never noticed. Then the real conversation: an 88%-and-climbing volume means growth outpacing capacity — set monitoring to alert earlier, and plan capacity rather than repeatedly firefighting. The framing: LVM turned a potential emergency-downtime into a two-minute live operation, which is the entire reason production storage is built on it.

E3. Documentation reference

TopicAuthoritative sourceVerified link
Block devices and partitionslsblk(8)lsblk(8)
Space usagedf(1), du(1)df(1) · du(1)
Mountingmount(8), fstab(5)mount(8) · fstab(5)
Open files (the deleted-open trap)lsof(8)lsof(8)
LVM, swap, inodeslvm(8), swapon(8), free(1), inode(7)lvm(8) · swapon(8) · free(1) · inode(7)
The kernel side beneath this moduleCompanion trackUntitled

E4. Self-assessment

Answer out loud, without notes. The section number tells you where to re-read.

  1. Name the three layers from raw disk to a usable file, and what turns each into the next. (A1–A3)
  2. What does lsblk show that df doesn't, and vice versa? (A1, B1)
  3. df vs du — what does each measure, and name two reasons they legitimately disagree. (B1–B2)
  4. Walk the full-disk incident procedure, command by command. (B3)
  5. A huge file was deleted but the disk stayed full. Explain the mechanism and the fix (and why rm isn't it). (B2, B3)
  6. What is mounting, what is a mount point, and why can't you unmount a "busy" filesystem? (C1)
  7. What is /etc/fstab, why reference filesystems by UUID, and what one command tests an edit safely? (C2)
  8. Explain LVM's three layers, and the two-step process to grow a filesystem live. (D1)
  9. Why is low "free" memory usually fine, and which column actually matters? When is swap a problem? (D2)
  10. How can a half-empty disk throw "No space left on device"? What command reveals it? (D3)
  11. Why can't you add inodes to a live ext4 filesystem, and what are the real fixes for inode exhaustion? (D3)
  12. Which storage problems are invisible to ls/du/find, and how do you see them anyway? (B2, D3)

E5. Sources

Interview-question sources used in this module (fetched and quoted verbatim during research, September 2026):

GeeksforGeeks — Linux Interview Questions (70+) (updated July 2026) · Turing — 100+ Linux Interview Questions (2025) · Adaface — 96 Linux Commands interview questions (September 2024).

Corpus honesty note: storage is reasonably covered in the banks — disk usage (df/du), permanent mounting, LVM, and largest-files questions all appear verbatim (quoted above). What the printed corpus under-asks are the counter-intuitive incidents — inode exhaustion, the df/du divergence, deleted-but-open files — which are precisely what a real storage interview and a real 3 a.m. page turn on. This module teaches those to the live "the disk is full but…" format, because that scenario, not command recall, is where storage competence is actually tested.

All documentation links on this page were fetched and confirmed reachable on 2 September 2026. df, du, lsblk, free, and the deleted-open-file demonstration were generated on a live Ubuntu 24.04 system; LVM outputs are the canonical format from an LVM-managed machine, since this authoring environment's disk is not LVM.

Next: storage is understood end to end. Time to connect the machine to the world — Module 13 — Networking.
Spotted a mistake or want something added? Send me a note.