Linux Storage & Filesystems
Block devices, partitions, filesystems (ext4/XFS/Btrfs), mounting and fstab, LVM, disk health (SMART), and resolving disk-pressure incidents.
Linux Storage & Filesystems
Managing block devices, partitions, filesystems, mounts, and LVM on Linux — plus the disk-pressure incidents that wake you at 3am.
Overview
Linux storage is a layered stack. A physical or virtual block device is carved into partitions; those partitions either hold a filesystem directly or are handed to LVM, which abstracts them into logical volumes you can grow, snapshot, and thin-provision. A filesystem (ext4, XFS, Btrfs) is created on whatever sits at the top, then attached to the directory tree at a mount point. Each layer has its own inspection and repair tooling, and understanding which layer a problem lives in is most of the battle during an incident.
This sheet is about managing storage — inspecting, creating, mounting, growing, and repairing it. For I/O throughput, queue depth, schedulers, and latency tuning, see Linux Performance Analysis; this sheet cross-references it rather than duplicating it.
flowchart TD
A["Block device<br/>/dev/sda, /dev/nvme0n1"] --> B["Partition<br/>/dev/sda1, /dev/nvme0n1p1"]
B --> C["LVM PV<br/>pvcreate"]
C --> D["Volume Group (VG)<br/>vgcreate"]
D --> E["Logical Volume (LV)<br/>lvcreate"]
B -.->|"or directly"| F["Filesystem<br/>ext4 / XFS / Btrfs"]
E --> F
F --> G["Mount point<br/>/mnt/data via fstab"]
Destructive-command warning runs through this whole sheet.
mkfs,parted/fdisk/sgdiskwrites,pvcreate,wipefs,fsck/xfs_repairon a mounted filesystem, andddto a block device all destroy data, often silently and instantly. Every such command below is flagged. The rule: run the read-only inspection form first, confirm you are pointed at the right device, then write. When you are mid-incident and tired, this is the discipline that saves you.
Inspecting Block Devices
Start here for every storage question: what devices exist, what is on them, and where they are mounted. All commands in this section are read-only and safe.
lsblk — the device tree
lsblk # Tree of devices, partitions, sizes, mountpoints
lsblk -f # Add FSTYPE, LABEL, UUID, FSAVAIL, FSUSE%
lsblk -p # Full device paths (/dev/sda1 not sda1)
lsblk -d # Disks only, no partitions
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID,MODEL,SERIAL
lsblk -t # Topology: alignment, sector size, scheduler
lsblk -J # JSON output (for scripting)
# Example -f output:
# NAME FSTYPE LABEL UUID FSUSE% MOUNTPOINT
# sda
# ├─sda1 vfat EFI A1B2-C3D4 12% /boot/efi
# └─sda2 LVM2_m kXyz...
# ├─vg0-root ext4 3f8a... 47% /
# └─vg0-swap swap 9c2b... [SWAP]
# nvme0n1
# └─nvme0n1p1 xfs data 7d1e... 63% /mnt/data
blkid, findmnt, and the kernel views
# blkid - filesystem UUIDs, labels, types (reads superblocks)
blkid # All detected filesystems
blkid /dev/sda1 # One device
blkid -o value -s UUID /dev/sda1 # Just the UUID (for fstab)
blkid -L mydata # Resolve a LABEL to a device
# findmnt - the friendly mount table (tree, filterable)
findmnt # Full mount tree
findmnt /mnt/data # What is mounted here
findmnt /dev/sda1 # Where is this device mounted
findmnt -t ext4,xfs # Filter by filesystem type
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS
findmnt --verify # Sanity-check /etc/fstab before a reboot
findmnt -D # df-like view with space usage
# Raw kernel views (no parsing helpers, always authoritative)
cat /proc/mounts # What the kernel currently has mounted
cat /proc/partitions # Block devices the kernel knows about
ls -l /sys/block/ # One entry per block device
cat /sys/block/sda/queue/rotational # 1 = spinning disk, 0 = SSD/NVMe
Device naming and stable paths
Kernel device names (/dev/sda, /dev/nvme0n1) are assigned at boot in discovery order and can change between boots or when you add a disk. Never reference them in fstab.
# Naming conventions
# SATA/SAS/USB: /dev/sda, partition /dev/sda1
# NVMe: /dev/nvme0n1, partition /dev/nvme0n1p1 (note the 'p')
# nvme0 = controller, n1 = namespace, p1 = partition
# virtio (VMs): /dev/vda, partition /dev/vda1
# MMC/SD: /dev/mmcblk0, partition /dev/mmcblk0p1
# Stable, persistent symlinks (use these in fstab / scripts)
ls -l /dev/disk/by-uuid/ # By filesystem UUID (most portable)
ls -l /dev/disk/by-label/ # By filesystem label
ls -l /dev/disk/by-id/ # By hardware serial (survives reslotting)
ls -l /dev/disk/by-path/ # By physical bus topology (slot-stable)
# Prefer by-uuid or by-label for filesystems; by-id when you mean a
# *specific physical disk* (e.g. a SMART check or a RAID member).
Partitioning
GPT is the modern default; use MBR only for legacy BIOS systems or disks under 2 TiB where something insists on it. MBR caps at 2 TiB and four primary partitions; GPT does neither and stores a backup table at the end of the disk.
All partition-table writes are destructive. Rewriting a partition table orphans every filesystem it described. Always
-lfirst, double-check the device node, and ideally back up the table (sgdisk --backup) before writing.
# Inspect first (read-only)
parted /dev/sda print # Partition table + type (gpt/msdos)
parted -l # All disks
fdisk -l /dev/sda # Classic listing
sgdisk -p /dev/sda # GPT-specific print
partx -s /dev/sda # Partitions as the kernel sees them
# --- Writes below: confirm the device node twice ---
# parted (scriptable, GPT-aware)
parted /dev/sdb mklabel gpt # Initialise a GPT label (WIPES table)
parted -a optimal /dev/sdb mkpart data ext4 0% 100% # Aligned, whole disk
parted /dev/sdb name 1 data
# sgdisk (purpose-built for GPT, great in scripts)
sgdisk --backup=/root/sdb.gpt /dev/sdb # Back up the table FIRST
sgdisk -n 1:0:0 -t 1:8300 -c 1:data /dev/sdb # New part, Linux fs type, name
sgdisk --load-backup=/root/sdb.gpt /dev/sdb # Restore if you fat-finger it
# Alignment matters: misaligned partitions cause read-modify-write on SSDs.
# parted's "optimal" and sgdisk's defaults both align to 1 MiB — leave them be.
# Tell the kernel to re-read the table without a reboot
partprobe /dev/sdb # Re-read partition table
partx -u /dev/sdb # Update kernel's view of changed partitions
Filesystems
ext4 vs XFS vs Btrfs
| ext4 | XFS | Btrfs | |
|---|---|---|---|
| Default on | Debian/Ubuntu | RHEL/Fedora/CentOS | openSUSE, Synology |
| Grow online | yes (resize2fs) |
yes (xfs_growfs) |
yes (btrfs fi resize) |
| Shrink | yes (offline) | no, ever | yes (online) |
| Snapshots | no (use LVM) | no (use LVM) | yes (native, CoW) |
| Checksums | metadata only | metadata only (v5/CRC) | data + metadata |
| Subvolumes | no | no | yes |
| Built-in RAID | no | no | yes (0/1/10; avoid 5/6) |
| Best for | general default, predictable | large files, parallel I/O, databases | snapshots, dedup, flexible layouts |
Rules of thumb: ext4 is the boring, reliable default. XFS scales better for large files and high parallelism (and is RHEL's choice) but cannot shrink — size it correctly up front. Btrfs gives you snapshots and checksums natively, but keep clear of its parity RAID (5/6) for anything you care about.
Creating filesystems
mkfsdestroys whatever is on the target. It does not ask. There is no undo. Confirm the device withlsblk -fimmediately before, and make sure it is not mounted.
# Inspect the target first — is it empty, is it mounted?
lsblk -f /dev/sdb1
findmnt /dev/sdb1 # No output = not mounted = safer to format
# ext4
mkfs.ext4 /dev/sdb1 # Default
mkfs.ext4 -L mydata /dev/sdb1 # With a label
mkfs.ext4 -m 1 /dev/sdb1 # Reserve 1% for root (default 5%)
mkfs.ext4 -E lazy_itable_init=0 /dev/sdb1 # Init tables now (slower, no first-mount lag)
# XFS
mkfs.xfs /dev/sdb1 # Default
mkfs.xfs -L mydata /dev/sdb1 # With a label
mkfs.xfs -f /dev/sdb1 # Force over an existing fs (DANGER)
# Btrfs
mkfs.btrfs /dev/sdb1 # Single device
mkfs.btrfs -L mydata /dev/sdb1
mkfs.btrfs -d raid1 -m raid1 /dev/sdb /dev/sdc # Mirrored across two devices
# Wipe old signatures if mkfs complains about a detected filesystem
wipefs -n /dev/sdb1 # DRY RUN: show what signatures exist
wipefs -a /dev/sdb1 # Actually erase them (DESTRUCTIVE)
Labels, tuning, and inspection
# ext4 — tune2fs
tune2fs -l /dev/sdb1 # Dump all superblock parameters (read-only)
tune2fs -L mydata /dev/sdb1 # Set label
tune2fs -m 1 /dev/sdb1 # Reduce reserved blocks to 1%
tune2fs -c 30 /dev/sdb1 # Force fsck every 30 mounts (0 = never)
tune2fs -o journal_data_writeback /dev/sdb1 # Journal mode (perf vs safety)
e2label /dev/sdb1 mydata # Shorthand for setting the label
dumpe2fs -h /dev/sdb1 # Superblock summary
# XFS — xfs_admin / xfs_info
xfs_info /mnt/data # Geometry of a mounted XFS (read-only)
xfs_admin -l /dev/sdb1 # Show label
xfs_admin -L mydata /dev/sdb1 # Set label (unmounted)
xfs_admin -U generate /dev/sdb1 # Regenerate UUID (after a clone)
Resizing
# ext4 — resize2fs (grow online; shrink only OFFLINE)
resize2fs /dev/vg0/data # Grow to fill the whole device
resize2fs /dev/vg0/data 50G # Grow/shrink to a specific size
# To shrink: unmount, fsck, then resize2fs to the smaller size, then shrink the LV.
# XFS — xfs_growfs (GROW ONLY, and only while MOUNTED)
xfs_growfs /mnt/data # Grow to fill the device
xfs_growfs -D 13107200 /mnt/data # Grow to N filesystem blocks
# XFS cannot shrink. Full stop. To "shrink", create a smaller fs and copy data.
# Btrfs
btrfs filesystem resize +10G /mnt/data # Grow by 10G (online)
btrfs filesystem resize -5G /mnt/data # Shrink by 5G (online)
btrfs filesystem resize max /mnt/data # Grow to fill device
The number-one resize footgun: growing the filesystem before growing the block device underneath it. With LVM, extend the LV first (or use
lvextend -rto do both atomically — see the LVM section). The filesystem can never be larger than its container.
Checking and repairing
Never run
fsckorxfs_repairon a mounted filesystem. At best it refuses; at worst it corrupts a live filesystem catastrophically. Unmount first (umount), or for the root filesystem, boot to rescue/single-user mode or use the distro's boot-time fsck.
# Check whether a filesystem is mounted before touching it
findmnt /dev/sdb1 # Output here means STOP — it is mounted
# ext4 — fsck / e2fsck
fsck -N /dev/sdb1 # Dry run: show what WOULD run, do nothing
e2fsck -n /dev/sdb1 # Read-only check, answer "no" to all repairs
e2fsck -f /dev/sdb1 # Force a full check (even if marked clean)
e2fsck -p /dev/sdb1 # Preen: auto-fix only safe problems
e2fsck -y /dev/sdb1 # Assume "yes" (last resort; can be aggressive)
e2fsck -b 32768 /dev/sdb1 # Use a backup superblock if primary is gone
# XFS — xfs_repair (XFS has no interactive fsck)
xfs_repair -n /dev/sdb1 # DRY RUN: report damage, change nothing
xfs_repair /dev/sdb1 # Actually repair (must be UNMOUNTED)
xfs_repair -L /dev/sdb1 # Zero a corrupt log — LAST RESORT, can lose data
# If a dirty XFS log blocks repair, mount-then-unmount replays it cleanly first.
# Btrfs
btrfs scrub start /mnt/data # Online: verify checksums, fix from good copies
btrfs scrub status /mnt/data
btrfs check --readonly /dev/sdb1 # Offline read-only check
btrfs check --repair /dev/sdb1 # DANGEROUS — only on btrfs devs' advice
Btrfs subvolumes and snapshots (briefly)
Btrfs subvolumes are independently-mountable, snapshottable trees inside one filesystem — no fixed-size partitioning needed. Snapshots are copy-on-write and near-instant.
btrfs subvolume create /mnt/data/@home # Create a subvolume
btrfs subvolume list /mnt/data # List subvolumes
btrfs subvolume snapshot /mnt/data/@home \
/mnt/data/@home_snap_$(date +%F) # Writable snapshot
btrfs subvolume snapshot -r /mnt/data/@home \
/mnt/data/@home_ro # Read-only snapshot (for backups)
btrfs subvolume delete /mnt/data/@home_snap_2026-06-15
# Mount a specific subvolume: mount -o subvol=@home /dev/sdb1 /home
Mounting and fstab
mount / umount
# Inspect what is mounted (read-only)
mount # All mounts (verbose; findmnt is nicer)
mount | column -t # Slightly more readable
# Mount
mount /dev/sdb1 /mnt/data # By device
mount UUID=7d1e-... /mnt/data # By UUID
mount LABEL=mydata /mnt/data # By label
mount -t xfs -o noatime /dev/sdb1 /mnt/data # Explicit type + options
mount -o remount,rw / # Remount root read-write (rescue)
mount -a # Mount everything in fstab not yet mounted
# Unmount
umount /mnt/data # Normal unmount
umount /dev/sdb1 # By device also works
umount -l /mnt/data # LAZY: detach now, free when last user closes it
umount -f /mnt/data # FORCE (mainly for unresponsive NFS)
Common mount options
| Option | Effect |
|---|---|
defaults |
rw,suid,dev,exec,auto,nouser,async |
noatime |
Don't update access times — a cheap, common I/O win |
relatime |
Update atime only if older than mtime (the kernel default) |
ro / rw |
Read-only / read-write |
nodev |
Ignore device nodes (security; use on data mounts) |
nosuid |
Ignore setuid/setgid bits (security) |
noexec |
Forbid execution of binaries (security; e.g. /tmp) |
nofail |
Boot even if this device is absent (essential for removable/optional) |
_netdev |
Wait for the network before mounting (NFS, iSCSI) |
discard |
Issue TRIM inline (prefer a periodic fstrim.timer instead) |
/etc/fstab
Six whitespace-separated fields per line:
# <file system> <mount point> <type> <options> <dump> <pass>
UUID=7d1e-... /mnt/data xfs defaults,noatime 0 0
LABEL=backup /mnt/backup ext4 defaults,nofail 0 2
/dev/vg0/root / ext4 defaults 0 1
UUID=A1B2-C3D4 /boot/efi vfat umask=0077 0 2
tmpfs /tmp tmpfs defaults,nosuid 0 0
# Field 5 (dump): almost always 0 — legacy backup flag.
# Field 6 (pass): fsck order. 1 = root only, 2 = other local fs, 0 = never.
Always validate fstab before rebooting. A bad line can drop the box to an emergency shell. After editing, run
findmnt --verifyandmount -a— ifmount -aerrors, fix it now, while you still have a shell. Usenofailon any non-essential device so its absence can't block boot.
systemd mount units and automount
systemd generates a transient .mount unit from every fstab entry, but you can also write them explicitly, and x-systemd.automount mounts a path lazily on first access — ideal for slow or network filesystems.
# fstab option for on-demand mounting
# UUID=... /mnt/slow xfs noauto,x-systemd.automount,x-systemd.idle-timeout=60 0 0
# noauto -> don't mount at boot
# x-systemd.automount -> mount on first access
# x-systemd.idle-timeout=60 -> unmount after 60s idle
systemctl daemon-reload # After editing fstab, regenerate units
systemctl status mnt-data.mount # Path / is mangled to a unit name
systemd-escape -p --suffix=mount /mnt/data # Compute that unit name
systemctl list-units --type=mount # All active mount units
Finding what blocks an unmount
umount failing with "target is busy" means a process has a file open or a CWD under the mount. Find and clear the offender before reaching for umount -l.
lsof /mnt/data # Processes with files open under the mount
lsof +D /mnt/data # Recursive (slower, thorough)
fuser -vm /mnt/data # Lighter; shows PIDs and access type
fuser -km /mnt/data # KILL everything using it (last resort, DANGER)
# fuser ships in the psmisc package — not always present on minimal installs
# (apt-get install psmisc); lsof is the more commonly-available fallback.
See the Open File Analysis with lsof section of Linux Performance Analysis for the full lsof/fuser treatment, including the deleted-but-open-file trap covered below.
LVM and Device Mapper
LVM inserts a flexible layer between physical disks and filesystems. Physical Volumes (PVs) are disks or partitions initialised for LVM; they pool into a Volume Group (VG); from that pool you carve Logical Volumes (LVs), which behave like resizable partitions. Underneath, the kernel's device-mapper does the actual block remapping — every LV is a dm-* device, visible under /dev/mapper/.
flowchart TD
subgraph PVs["Physical Volumes"]
P1["/dev/sdb1 (PV)"]
P2["/dev/sdc1 (PV)"]
end
P1 --> VG["Volume Group: vg0<br/>(pooled extents)"]
P2 --> VG
VG --> L1["LV: root → /"]
VG --> L2["LV: data → /mnt/data"]
VG --> L3["LV: swap → [SWAP]"]
L1 --> DM["device-mapper /dev/mapper/vg0-root"]
L2 --> DM2["device-mapper /dev/mapper/vg0-data"]
Reporting (read-only — start here)
pvs # PV summary: device, VG, size, free
vgs # VG summary: name, #PV, #LV, size, free
lvs # LV summary: name, VG, size, attrs
lvs -a -o +devices # LVs incl. hidden + which PVs back them
pvdisplay # Verbose PV info
vgdisplay # Verbose VG info (note "Free PE / Size")
lvdisplay # Verbose LV info
dmsetup ls # Raw device-mapper targets
ls -l /dev/mapper/ # The dm devices LVM created
Building the stack
pvcreatewrites LVM metadata and will clobber an existing filesystem on the target. Confirm withlsblk -fthat the partition is empty (or that you mean to consume it) before running it.
# 1. Initialise PVs (DESTRUCTIVE to existing fs on the device)
pvcreate /dev/sdb1 /dev/sdc1
# 2. Create a VG from one or more PVs
vgcreate vg0 /dev/sdb1 /dev/sdc1
# 3. Create LVs from the VG's free space
lvcreate -L 50G -n data vg0 # 50 GiB LV named "data"
lvcreate -l 100%FREE -n bulk vg0 # Use all remaining free extents
lvcreate -l 50%VG -n half vg0 # Half of the whole VG
# 4. Put a filesystem on the LV and mount it (mkfs is DESTRUCTIVE)
mkfs.ext4 /dev/vg0/data
mount /dev/vg0/data /mnt/data # Also at /dev/mapper/vg0-data
Extending (the common case)
# Add a new disk's capacity to an existing VG
pvcreate /dev/sdd1 # Initialise the new disk (DESTRUCTIVE)
vgextend vg0 /dev/sdd1 # Pool it into the VG
# Grow an LV AND its filesystem in one step (-r resizes the fs too)
lvextend -r -L +20G /dev/vg0/data # Add 20 GiB and grow the fs
lvextend -r -l +100%FREE /dev/vg0/data # Consume all remaining VG free space
# -r handles ext4 (resize2fs) and XFS (xfs_growfs) for you. This is the safe path:
# it grows the LV first, then the filesystem, in the correct order.
Thin provisioning
Thin pools let LVs claim more space than physically exists, allocating on write. Powerful for many small volumes and snapshots — but monitor pool usage, because a full thin pool causes I/O errors across every volume in it.
lvcreate -L 100G --thinpool pool0 vg0 # Create a 100G thin pool
lvcreate -V 500G --thin -n vol1 vg0/pool0 # 500G *virtual* LV in a 100G pool
lvs -o +data_percent,metadata_percent vg0 # WATCH these — full pool = outage
LVM snapshots
# Traditional (thick) snapshot — needs reserved CoW space; fills up if it overflows
lvcreate -L 5G -s -n data_snap /dev/vg0/data # 5G snapshot of "data"
mount -o ro /dev/vg0/data_snap /mnt/snap # Mount it (e.g. for a consistent backup)
lvremove /dev/vg0/data_snap # Remove when done
lvconvert --merge /dev/vg0/data_snap # Roll the origin BACK to the snapshot
# Thin snapshots are instant and don't pre-reserve space (preferred where available)
lvcreate -s -n vol1_snap vg0/vol1
Disk Health (SMART)
SMART surfaces failing-drive warning signs early. The three attributes that actually predict failure are Reallocated_Sector_Ct, Current_Pending_Sector, and Offline_Uncorrectable — any non-zero value that is growing means replace the drive.
# Discover SMART-capable devices
smartctl --scan
# Read health and attributes (read-only)
smartctl -H /dev/sda # Overall PASSED/FAILED verdict
smartctl -a /dev/sda # All attributes and the error log
smartctl -i /dev/sda # Identity: model, serial, firmware
smartctl -A /dev/sda | grep -E 'Reallocated_Sector|Pending|Uncorrectable'
# Run self-tests (background, non-destructive to data)
smartctl -t short /dev/sda # ~2 min quick test
smartctl -t long /dev/sda # Full surface scan (hours)
smartctl -l selftest /dev/sda # View self-test results
# NVMe drives expose a different log
nvme smart-log /dev/nvme0 # Wear, media errors, temperature, spare %
nvme list # Enumerate NVMe namespaces
# badblocks — surface scan (use the read-only form!)
badblocks -sv /dev/sdb # READ-ONLY non-destructive scan with progress
# badblocks -w /dev/sdb # WRITE-mode test — DESTROYS ALL DATA. Don't.
badblocks -w(write-mode) erases the entire device. The default read-only-svform is the one you want during an incident. SMART self-tests (-t) are safe and do not touch your data.
Capacity and Disk-Pressure Incidents
Measuring space and inodes
df -h # Space, human-readable
df -h /mnt/data # One mount
df -i # INODE usage — checked far too rarely
df -hT # Include filesystem type
df -h -x tmpfs -x devtmpfs # Exclude virtual filesystems
# A disk can be "full" two ways: out of blocks (df -h) OR out of inodes (df -i).
# Millions of tiny files exhaust inodes while df -h still shows free space —
# writes fail with ENOSPC and df -h "lies". ALWAYS check df -i too.
Finding what is eating the space
du -sh /var/* # Size of each thing under /var
du -sh /var/* 2>/dev/null | sort -rh | head -20 # Biggest offenders
du -xh --max-depth=1 / | sort -rh | head # -x stays on one filesystem
du -sh --exclude='*.log' /srv # Exclude a pattern
ncdu /var # Interactive disk-usage explorer (install it)
ncdu -x / # Stay on one filesystem
duwalks the tree and can be slow on huge directories;dfreads the superblock and is instant. When they disagree —dffull,ducan't account for it — suspect a deleted-but-open file (next section).
The deleted-but-open-file trap
A classic incident: df says the disk is full, but du cannot find the space. A process is holding a deleted file open — the directory entry is gone, but the inode and its blocks stay allocated until the last file descriptor closes. Truncating logs without restarting the writer is the usual cause.
lsof -nP +L1 # Files with link count 0 still held open
lsof | grep '(deleted)' # Classic form; shows PID + deleted path
ls -l /proc/<pid>/fd | grep deleted # Confirm via the holding process's FDs
# Reclaim WITHOUT a restart by truncating through the open FD:
: > "/proc/<pid>/fd/<n>" # Empty the still-open file in place
# Otherwise: restart or HUP the process so it releases the descriptor.
This is cross-referenced from the lsof section of Linux Performance Analysis, which covers the descriptor-leak angle in more depth.
Growing a full LVM volume online, end to end
The reason to put / or /var on LVM: you can grow them under load with zero downtime.
# 1. Confirm the pressure and the layout
df -h /var
lvs ; vgs # Is there free space in the VG?
# 2a. If the VG has free extents, just extend the LV + fs in one step:
lvextend -r -l +100%FREE /dev/vg0/var
# 2b. If the VG is also full, add a disk first, THEN extend:
pvcreate /dev/sdd1 # (DESTRUCTIVE to /dev/sdd1)
vgextend vg0 /dev/sdd1
lvextend -r -L +50G /dev/vg0/var
# 3. Verify — the new space is live immediately, no remount needed
df -h /var
If
/boot(not on LVM) fills with old kernels, this won't help — clear old kernel packages with your package manager instead (apt autoremove --purge,dnf remove old-kernels). Know which of your mounts are on LVM before the incident.
Quotas (briefly)
# Mount with quota support, then:
quotacheck -cugm /home # Initialise quota files
quotaon /home # Enable quotas
edquota -u alice # Edit a user's soft/hard limits
repquota -s /home # Report usage vs limits
# XFS uses project quotas instead: mount -o prjquota, then xfs_quota.
Quick Reference
| Task | Command |
|---|---|
| List devices + filesystems | lsblk -f |
| Get a UUID for fstab | blkid -o value -s UUID /dev/sda1 |
| What's mounted where | findmnt |
| Validate fstab | findmnt --verify |
| Disk space / inodes | df -h / df -i |
| Biggest directories | du -sh /path/* | sort -rh | head |
| Interactive usage | ncdu -x / |
| Create ext4 / XFS | mkfs.ext4 /dev/X / mkfs.xfs /dev/X |
| Grow LV + filesystem | lvextend -r -l +100%FREE /dev/vg/lv |
| Grow mounted XFS | xfs_growfs /mnt/point |
| Check ext4 (offline) | e2fsck -f /dev/X |
| Repair XFS (offline) | xfs_repair /dev/X |
| LVM overview | pvs ; vgs ; lvs |
| Drive health | smartctl -H /dev/sda |
| NVMe health | nvme smart-log /dev/nvme0 |
| Find deleted-open files | lsof -nP +L1 |
| What blocks an unmount | fuser -vm /mnt/data |
| Re-read partition table | partprobe /dev/sda |
Common Issues and Solutions
Disk full ("No space left on device")
df -h # Confirm which filesystem
df -i # Inodes exhausted? Common with many tiny files
du -xh --max-depth=1 /var | sort -rh | head # Locate the consumer (-x: one fs)
lsof -nP +L1 # df full but du can't find it → deleted-open file
Fix: delete/rotate the offending files; if inodes are exhausted, removing files frees them — there is no online resize for ext4 inode count (it's fixed at mkfs). If on LVM, lvextend -r to add space. If a deleted-open file, restart the holding process.
Filesystem won't unmount ("target is busy")
fuser -vm /mnt/data # Who is using it
lsof +D /mnt/data # Which files are open
Fix: stop the offending process, or cd out of the mount if it's your own shell. Use umount -l (lazy) to detach immediately and clean up when the last user exits; fuser -km to kill everything (last resort).
Filesystem went read-only after an error
The kernel remounts read-only on detecting corruption (a safety measure). Check dmesg/journalctl -k for EXT4-fs error or XFS ... corruption, then check the disk's SMART status — a read-only flip is often a symptom of failing hardware.
dmesg | grep -iE 'ext4|xfs|i/o error|remount'
smartctl -a /dev/sda | grep -iE 'pending|reallocated|uncorrect'
Fix: unmount, e2fsck -f / xfs_repair, remount. If SMART shows growing bad sectors, replace the drive — repairing on dying hardware just postpones data loss.
LVM out of space (VG full)
vgs # "VFree" near zero → no extents to allocate
Fix: add a disk (pvcreate then vgextend), then lvextend -r. For thin pools, watch data_percent — a full thin pool causes I/O errors on every thin volume in it; extend the pool with lvextend before it fills.
Failing drive (SMART warnings)
smartctl -H /dev/sda # FAILED verdict
smartctl -A /dev/sda | grep -E 'Reallocated_Sector|Pending|Uncorrectable'
Fix: a growing reallocated/pending/uncorrectable count means imminent failure. Get a current backup now (see your backup tooling), then replace the drive. On RAID/LVM mirrors, fail and replace the member rather than waiting for it to die under load.
Related Topics
- Linux Performance Analysis — I/O throughput, latency, schedulers, and the full
lsoftreatment - Linux CLI — core command-line fluency these workflows build on
- systemd — mount units,
x-systemd.automount, and service resource control - SELinux / AppArmor — mount-point labelling and access control on data volumes
- Linux/BSD/macOS network tools — for NFS, iSCSI, and
_netdevnetwork-backed mounts