Linux Disk and Storage Management Explained — Partitions, LVM and Real-World Patterns
Every production outage I've ever seen that started with 'disk' in the alert was caused by someone who treated storage as an afterthought. A full root partition kills web servers, a misconfigured filesystem destroys databases, and a missing mount point in /etc/fstab means your server reboots into chaos at 3 AM. Storage management isn't glamorous, but it is the difference between a system that hums along and one that pages you on a Friday night.
The problem is that most tutorials show you the commands and stop there. They'll tell you to run mkfs.ext4 without explaining that formatting is irreversible and takes seconds. They'll show you mount without mentioning it evaporates on reboot unless you wire it into /etc/fstab. The gap between 'ran the command in a tutorial' and 'confidently managing storage on a live server' is exactly where people get hurt.
By the end of this article you'll know how to inspect a disk from scratch, partition it intentionally, format it with the right filesystem for your workload, mount it persistently, and use LVM to manage storage dynamically when your needs change. These are the skills you actually need on the job — not just for passing an exam.
Inspecting What You Have — Reading the Disk Landscape Before Touching Anything
The first rule of storage management is: never run a destructive command on a disk you haven't fully inspected. This sounds obvious, but under pressure people confuse /dev/sda with /dev/sdb and wipe the wrong drive. It happens more than anyone admits.
lsblk is your safest starting point. It reads block device info from sysfs without touching the disk itself — no risk, no side effects. It shows you the full device tree: physical drives, their partitions, and any logical volumes sitting on top. fdisk -l goes deeper, showing partition types, sizes, and sector alignment, but it requires root.
df -h tells you about mounted filesystems — what's actually in use right now. Note the difference: lsblk shows you everything attached to the system, df -h shows only what's mounted and accessible. A disk can exist on lsblk and be completely invisible to df -h if nobody's mounted it yet. Understanding this distinction stops a whole class of 'where did my disk go?' confusion.
The UUID shown in blkid is critical — always use UUIDs in /etc/fstab, not device names like /dev/sdb1. Device names are assigned at boot time and can change if you add or remove hardware. UUIDs are permanent identifiers burned into the filesystem itself.
#!/bin/bash # inspect_disk_landscape.sh # Safe read-only commands to fully understand your storage before making any changes. # Run as root (or with sudo) for full output. echo "=== BLOCK DEVICE TREE (lsblk) ===" # Shows all block devices, their sizes, types, and mount points. # NAME: device name | SIZE: total size | TYPE: disk/part/lvm | MOUNTPOINT: where it's mounted lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID echo "" echo "=== PARTITION DETAILS (fdisk) ===" # fdisk -l lists every partition on every disk with sector info and partition type. # This is the authoritative view of what's physically on the disk. sudo fdisk -l /dev/sda echo "" echo "=== MOUNTED FILESYSTEM USAGE (df) ===" # df -h shows only MOUNTED filesystems with human-readable sizes. # Use this to see actual disk usage — not just what exists. df -h --output=source,size,used,avail,pcent,target echo "" echo "=== FILESYSTEM UUIDs (blkid) ===" # blkid reads filesystem metadata — UUID, TYPE, LABEL. # Always use UUID in /etc/fstab — never /dev/sdX names. sudo blkid
NAME SIZE TYPE FSTYPE MOUNTPOINT UUID
sda 100G disk
├─sda1 512M part vfat /boot/efi A1B2-C3D4
├─sda2 1G part ext4 /boot a1b2c3d4-1111-2222-3333-aabbccddeeff
└─sda3 98.5G part LVM2_member b2c3d4e5-2222-3333-4444-bbccddeeff00
├─vg0-root 20G lvm ext4 / c3d4e5f6-3333-4444-5555-ccddeeff0011
├─vg0-home 40G lvm ext4 /home d4e5f6a7-4444-5555-6666-ddeeff001122
└─vg0-data 38G lvm xfs /data e5f6a7b8-5555-6666-7777-eeff00112233
sdb 500G disk
└─sdb1 500G part xfs /mnt/backups f6a7b8c9-6666-7777-8888-ff0011223344
=== PARTITION DETAILS (fdisk) ===
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Disk model: Virtual Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Device Start End Sectors Size Type
/dev/sda1 2048 1050623 1048576 512M EFI System
/dev/sda2 1050624 3147775 2097152 1G Linux filesystem
/dev/sda3 3147776 209715166 206567391 98.5G Linux LVM
=== MOUNTED FILESYSTEM USAGE (df) ===
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg0-root 20G 8.1G 10.6G 44% /
/dev/sda2 976M 201M 708M 23% /boot
/dev/sda1 511M 5.2M 506M 2% /boot/efi
/dev/mapper/vg0-home 40G 15G 23G 39% /home
/dev/mapper/vg0-data 38G 22G 14G 59% /data
/dev/sdb1 500G 87G 413G 18% /mnt/backups
=== FILESYSTEM UUIDs (blkid) ===
/dev/sda1: UUID="A1B2-C3D4" TYPE="vfat" PARTUUID="..."
/dev/sda2: UUID="a1b2c3d4-1111-2222-3333-aabbccddeeff" TYPE="ext4"
/dev/sda3: UUID="b2c3d4e5-2222-3333-4444-bbccddeeff00" TYPE="LVM2_member"
/dev/mapper/vg0-root: UUID="c3d4e5f6-3333-4444-5555-ccddeeff0011" TYPE="ext4"
/dev/mapper/vg0-data: UUID="e5f6a7b8-5555-6666-7777-eeff00112233" TYPE="xfs"
Partitioning, Formatting and Mounting — Preparing a New Disk From Scratch
When a fresh disk arrives — whether it's a new SSD in a bare-metal server or a new EBS volume attached to an EC2 instance — it's a blank slate. No partition table, no filesystem, no mount point. Before any application can write data to it, you need to walk through three distinct steps: partition, format, mount.
Partitioning with gdisk (for GPT) or fdisk (for MBR) defines the logical boundaries on the disk. For any disk over 2TB or any UEFI system, use GPT. For older systems or VMs where you know it's MBR, fdisk is fine. The partition table is just metadata that tells the OS where one region ends and another begins.
Formatting writes a filesystem into that partition. ext4 is the safe, well-understood default for general-purpose workloads — it has journaling, solid fsck tooling, and decades of battle testing. xfs is better for large files and high-throughput workloads (think log aggregation, big data). Don't overthink it for most use cases: ext4 unless you have a specific reason.
Mounting connects the formatted partition to a directory in the filesystem tree. The mount command does it immediately, but it vanishes on reboot. The /etc/fstab file makes it permanent. Every mounted filesystem you care about needs an entry there.
#!/bin/bash # partition_format_mount.sh # Full walkthrough: take a raw disk (/dev/sdb) and make it usable. # WARNING: This DESTROYS all data on /dev/sdb. Verify the device name first. # Prerequisites: run as root. Confirm target disk with: lsblk | grep sdb TARGET_DISK="/dev/sdb" # The raw disk we're preparing PARTITION="/dev/sdb1" # The partition we'll create MOUNT_DIR="/mnt/appdata" # Where we'll attach it in the filesystem tree FS_LABEL="appdata-vol" # Human-readable label (helpful in logs and blkid) # --- STEP 1: PARTITION THE DISK --- # gdisk creates a GPT partition table (correct for disks > 2TB and modern systems). # We feed commands via heredoc to automate the interactive prompt: # n = new partition | 1 = partition number | defaults for start/end (use whole disk) # 8300 = Linux filesystem partition type code | w = write and exit echo "Creating GPT partition table on ${TARGET_DISK}..." sudo gdisk ${TARGET_DISK} <<EOF n 1 8300 w yes EOF # Inform the kernel about the new partition layout without rebooting. # partprobe reads the new partition table and updates the kernel's view. sudo partprobe ${TARGET_DISK} sleep 2 # Brief pause — kernel needs a moment to register the new partition # Confirm the partition was created echo "Partition layout after gdisk:" lsblk ${TARGET_DISK} # --- STEP 2: FORMAT THE PARTITION --- # mkfs.ext4 writes an ext4 filesystem into the partition. # -L sets a human-readable label — visible in lsblk, blkid, and system logs. # This step is IRREVERSIBLE on a partition with existing data. echo "Formatting ${PARTITION} as ext4..." sudo mkfs.ext4 -L ${FS_LABEL} ${PARTITION} # --- STEP 3: CREATE MOUNT POINT --- # The mount directory must exist before you can mount anything to it. # mkdir -p creates it (and any missing parents) without erroring if it exists. sudo mkdir -p ${MOUNT_DIR} # --- STEP 4: MOUNT TEMPORARILY (to verify it works) --- # mount attaches the filesystem — but this survives only until next reboot. sudo mount ${PARTITION} ${MOUNT_DIR} echo "Temporary mount successful. Testing write access..." echo "storage_test" | sudo tee ${MOUNT_DIR}/write_test.txt > /dev/null # --- STEP 5: GET UUID FOR FSTAB --- # Never put /dev/sdb1 in fstab. Get the stable UUID instead. DISK_UUID=$(sudo blkid -s UUID -o value ${PARTITION}) echo "UUID for ${PARTITION}: ${DISK_UUID}" # --- STEP 6: ADD TO /etc/fstab FOR PERSISTENT MOUNTING --- # Backup fstab first — a broken fstab prevents the system from booting. sudo cp /etc/fstab /etc/fstab.backup.$(date +%Y%m%d_%H%M%S) # Append the fstab entry: # UUID=... /mnt/appdata ext4 defaults,nofail 0 2 # 'nofail' means the system still boots even if this disk is missing. # The final '2' means fsck runs on this partition after the root partition. echo "UUID=${DISK_UUID} ${MOUNT_DIR} ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab # Verify fstab is valid by mounting everything listed in it. # If this errors, restore from backup: sudo cp /etc/fstab.backup.* /etc/fstab sudo mount -a && echo "fstab validation passed — all entries mounted successfully." # Final check df -h ${MOUNT_DIR}
Partition layout after gdisk:
NAME SIZE TYPE FSTYPE MOUNTPOINT
sdb 200G disk
└─sdb1 200G part
Formatting /dev/sdb1 as ext4...
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 52428800 4k blocks and 13107200 inodes
Filesystem UUID: f7a8b9c0-7777-8888-9999-001122334455
Superblock backups stored on blocks: 32768, 98304, 163840 ...
Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done
Temporary mount successful. Testing write access...
UUID for /dev/sdb1: f7a8b9c0-7777-8888-9999-001122334455
fstab validation passed — all entries mounted successfully.
Filesystem Size Used Avail Use% Mounted on
/dev/sdb1 197G 28K 187G 1% /mnt/appdata
LVM — Dynamic Storage That Grows With Your Application
Here's the problem with raw partitions: they're static. You create a 50GB partition for your database, the database grows to 48GB, and now you're racing against time. Your only options are to resize the partition (risky, requires unmounting on most filesystems) or provision a new disk and move data. Neither is fun at 2 AM.
LVM — Logical Volume Manager — solves this by adding an abstraction layer between physical disks and the filesystems sitting on them. Instead of your filesystem sitting directly on /dev/sdb1, it sits on a logical volume that can be expanded by simply adding more physical storage to the underlying pool, called a Volume Group.
The mental model has three layers. Physical Volumes (PVs) are the raw disks or partitions you hand to LVM. A Volume Group (VG) is the pool — LVM combines all your PVs into one big storage bucket. Logical Volumes (LVs) are carved out of that pool and behave like normal partitions from the filesystem's perspective. The magic is that you can extend an LV while it's live and mounted, without unmounting or stopping the application.
This is why nearly every production Linux server uses LVM for everything except /boot. It's not complexity for its own sake — it's the ability to respond to storage demands without downtime.
#!/bin/bash # lvm_setup_and_extend.sh # Demonstrates: creating an LVM stack from scratch AND extending a full volume live. # Scenario: web app data volume (/dev/sdc) is full. We add a new disk (/dev/sdd) # and expand the logical volume online — zero downtime. # ============================================================ # PART 1: BUILD AN LVM STACK ON A FRESH DISK # ============================================================ NEW_DISK="/dev/sdc" # Physical disk to hand to LVM VOLUME_GROUP="webdata_vg" # Our pool — think of it as the storage bucket LOGICAL_VOLUME="webapp_lv" # What the application actually uses LV_SIZE="30G" # Initial size carved from the pool MOUNT_POINT="/var/www/appdata" # Where the app reads and writes echo "=== STEP 1: Create Physical Volume ===" # pvcreate writes LVM metadata to the disk, marking it as LVM-managed. # After this, the disk belongs to LVM and should not be partitioned manually. sudo pvcreate ${NEW_DISK} # Verify the PV was created sudo pvdisplay ${NEW_DISK} echo "" echo "=== STEP 2: Create Volume Group ===" # vgcreate creates the storage pool and adds the PV to it. # You can add multiple PVs at creation: vgcreate my_vg /dev/sdc /dev/sdd sudo vgcreate ${VOLUME_GROUP} ${NEW_DISK} # Verify the VG — note 'VG Size' shows total available storage in the pool sudo vgdisplay ${VOLUME_GROUP} echo "" echo "=== STEP 3: Create Logical Volume ===" # lvcreate carves out a chunk of the VG as a usable logical volume. # -L specifies size | -n specifies the name sudo lvcreate -L ${LV_SIZE} -n ${LOGICAL_VOLUME} ${VOLUME_GROUP} # The LV is now available as a block device at this path: # /dev/webdata_vg/webapp_lv (also accessible via /dev/mapper/webdata_vg-webapp_lv) echo "LV device path: /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME}" echo "" echo "=== STEP 4: Format and Mount the Logical Volume ===" sudo mkfs.ext4 -L webapp-data /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME} sudo mkdir -p ${MOUNT_POINT} sudo mount /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME} ${MOUNT_POINT} # Add to fstab for persistence (using device mapper path — also stable with LVM) DEVICE_PATH="/dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME}" echo "${DEVICE_PATH} ${MOUNT_POINT} ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab df -h ${MOUNT_POINT} # ============================================================ # PART 2: EXTENDING THE VOLUME ONLINE (ZERO DOWNTIME) # ============================================================ # Scenario: 6 months later, /var/www/appdata is at 90% capacity. # We have a new disk /dev/sdd attached. Extend without unmounting. EXTRA_DISK="/dev/sdd" # New disk arriving in the system EXTEND_BY="+50G" # How much to add to the logical volume echo "" echo "=== EXTEND: Add new disk to the VG pool ===" # pvcreate the new disk first, then vgextend adds it to our existing pool. sudo pvcreate ${EXTRA_DISK} sudo vgextend ${VOLUME_GROUP} ${EXTRA_DISK} # Confirm the VG now has more free space sudo vgs ${VOLUME_GROUP} echo "" echo "=== EXTEND: Grow the logical volume ===" # lvextend grows the LV. -r flag is critical — it also resizes the filesystem # inside the LV at the same time. Without -r, you'd have a bigger LV but # the filesystem inside wouldn't know about the extra space. sudo lvextend -L ${EXTEND_BY} -r /dev/mapper/${VOLUME_GROUP}-${LOGICAL_VOLUME} # The application is still running. No unmount. No restart. Verify the new size: df -h ${MOUNT_POINT}
Physical volume "/dev/sdc" successfully created.
--- Physical volume ---
PV Name /dev/sdc
VG Name
PV Size 100.00 GiB / not usable 4.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 25599
Free PE 25599
=== STEP 2: Create Volume Group ===
Volume group "webdata_vg" successfully created
--- Volume group ---
VG Name webdata_vg
VG Size <100.00 GiB
PE Size 4.00 MiB
Total PE 25599
Free PE / Size 25599 / <100.00 GiB
=== STEP 3: Create Logical Volume ===
Logical volume "webapp_lv" created.
LV device path: /dev/mapper/webdata_vg-webapp_lv
=== STEP 4: Format and Mount the Logical Volume ===
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/webdata_vg-webapp_lv 30G 24K 28G 1% /var/www/appdata
=== EXTEND: Add new disk to the VG pool ===
Physical volume "/dev/sdd" successfully created.
Volume group "webdata_vg" successfully extended
VG #PV #LV #SN Attr VSize VFree
webdata_vg 2 1 0 wz--n- 199.99g 169.99g
=== EXTEND: Grow the logical volume ===
Size of logical volume webdata_vg/webapp_lv changed from 30.00 GiB to 80.00 GiB.
Logical volume webdata_vg/webapp_lv successfully resized.
resize2fs 1.46.5
Resizing the filesystem on /dev/mapper/webdata_vg-webapp_lv to 20971520 (4k) blocks.
The filesystem on /dev/mapper/webdata_vg-webapp_lv is now 20971520 (4k) blocks long.
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/webdata_vg-webapp_lv 79G 24K 75G 1% /var/www/appdata
Monitoring, Troubleshooting and the /etc/fstab Deep Dive
Understanding how to provision storage is half the job. The other half is knowing when something's going wrong before it takes down your application, and being able to diagnose it fast.
The biggest production risk is a full disk — but the sneaky version is inodes running out before disk space does. Every file on an ext4 filesystem consumes one inode. A directory full of millions of tiny temp files (log shards, session files, cache chunks) can exhaust inodes while df -h shows 40% free space. The symptom is 'No space left on device' errors even though the disk looks fine. df -i reveals the truth.
For performance visibility, iostat from the sysstat package shows read/write throughput and I/O wait per device. High iowait on a specific device tells you whether your application is CPU-bound or storage-bound. iotop shows which processes are doing the most I/O right now — invaluable for finding a runaway process.
For /etc/fstab specifically: the six fields matter. The 'dump' field (5th, almost always 0) controls backup utilities. The 'pass' field (6th) controls fsck order — root should be 1, everything else 2 or 0 to skip. A wrong pass value on a network filesystem causes boot hangs because fsck tries to check an NFS share that isn't available yet.
#!/bin/bash # storage_monitoring_and_diagnostics.sh # Production-grade monitoring and diagnostics for Linux storage. # Covers: disk usage, inode exhaustion, I/O performance, fstab validation. echo "============================================" echo " STORAGE HEALTH DASHBOARD" echo "============================================" # --- DISK SPACE BY FILESYSTEM --- echo "" echo "[1] DISK SPACE USAGE (human-readable)" # -T shows filesystem type — useful for spotting unexpected tmpfs mounts eating space df -hT --exclude-type=tmpfs --exclude-type=devtmpfs # --- INODE USAGE — THE SILENT KILLER --- echo "" echo "[2] INODE USAGE — check this if you see 'No space left on device' with free space" # PCent shows inode usage percentage. 100% means no new files can be created, # even if gigabytes of block space remain. df -i --exclude-type=tmpfs --exclude-type=devtmpfs # --- FIND DIRECTORIES CONSUMING THE MOST DISK SPACE --- echo "" echo "[3] TOP 10 LARGEST DIRECTORIES under /var (common culprit for space issues)" # du -s gives summary per directory. sort -rh sorts by size, largest first. # --exclude prevents following bind mounts and getting misleading totals. sudo du -sh /var/*/ 2>/dev/null | sort -rh | head -10 # --- I/O PERFORMANCE SNAPSHOT --- echo "" echo "[4] DISK I/O STATISTICS (3-second sample)" # iostat -xd: x=extended stats, d=devices only (skip CPU) # Key columns: r/s (reads per sec), w/s (writes per sec), %util (device saturation) # %util near 100% = storage bottleneck sudo iostat -xd 1 3 2>/dev/null || echo "Install sysstat: sudo apt install sysstat" # --- WHICH PROCESS IS HAMMERING DISK RIGHT NOW --- echo "" echo "[5] TOP I/O PROCESSES (requires iotop)" # -b = batch mode (non-interactive) | -n 1 = one iteration | -o = only show active processes sudo iotop -b -n 1 -o 2>/dev/null || echo "Install iotop: sudo apt install iotop" # --- VALIDATE /etc/fstab WITHOUT REBOOTING --- echo "" echo "[6] FSTAB VALIDATION" echo "Current /etc/fstab entries (non-comment lines):" grep -v '^#' /etc/fstab | grep -v '^$' | column -t echo "" echo "Testing fstab by running: mount -a (mounts everything in fstab not already mounted)" # mount -a is safe to run on a live system — it only mounts things not yet mounted. # Any error here means your fstab has a problem that would break the next reboot. sudo mount -a 2>&1 && echo "✓ fstab OK — all entries valid" || echo "✗ fstab ERROR — fix before rebooting!" # --- SMART DISK HEALTH (physical drives only) --- echo "" echo "[7] DISK HEALTH CHECK (smartctl)" # smartctl reads the drive's self-monitoring data. # 'PASSED' = drive reports no hardware issues. # 'FAILED' = replace the drive NOW before it dies completely. sudo smartctl -H /dev/sda 2>/dev/null || echo "Install smartmontools: sudo apt install smartmontools" # --- ALERT THRESHOLD: WARN IF ANY FILESYSTEM OVER 80% --- echo "" echo "[8] CAPACITY ALERTS (>80% used)" df -h --output=source,pcent,target | awk 'NR>1 && $2+0 > 80 { print "ALERT: " $1 " at " $2 " capacity — mount: " $3 }' || echo "All filesystems under 80% — looking healthy."
STORAGE HEALTH DASHBOARD
============================================
[1] DISK SPACE USAGE (human-readable)
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/vg0-root ext4 20G 8.1G 10G 44% /
/dev/sda2 ext4 976M 201M 708M 23% /boot
/dev/mapper/vg0-home ext4 40G 15G 23G 39% /home
/dev/mapper/webdata_vg-webapp_lv ext4 79G 24G 51G 31% /var/www/appdata
/dev/sdb1 xfs 500G 87G 413G 18% /mnt/backups
[2] INODE USAGE
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/vg0-root 1310720 204831 1105889 16% /
/dev/mapper/webdata_vg-webapp_lv 5242880 4900012 342868 94% /var/www/appdata
[3] TOP 10 LARGEST DIRECTORIES under /var
4.2G /var/log/
1.8G /var/cache/
890M /var/lib/
210M /var/www/
[4] DISK I/O STATISTICS
Device r/s w/s rkB/s wkB/s await %util
sda 2.14 18.72 87.4 742.3 3.21 12.4
sdb 0.08 45.61 3.2 1821.4 1.84 67.2
[5] TOP I/O PROCESSES
Total DISK READ: 87.4 KiB/s | Total DISK WRITE: 2.5 MiB/s
PID USER DISK READ DISK WRITE COMMAND
14821 mysql 0.00 B/s 1.9 MiB/s mysqld
9034 www 0.00 B/s 612.0 KiB/s php-fpm
[6] FSTAB VALIDATION
UUID=c3d4e5f6... / ext4 defaults 0 1
UUID=a1b2c3d4... /boot ext4 defaults 0 2
UUID=A1B2-C3D4 /boot/efi vfat umask=0077 0 2
/dev/mapper/... /var/www/app ext4 defaults,nofail 0 2
✓ fstab OK — all entries valid
[8] CAPACITY ALERTS (>80% used)
ALERT: /dev/mapper/webdata_vg-webapp_lv at 94% inode capacity — mount: /var/www/appdata
| Aspect | ext4 | xfs | Raw Partition (no LVM) |
|---|---|---|---|
| Best Use Case | General purpose, boot volumes, home dirs | Large files, high-throughput, databases, log aggregation | Simple, single-purpose disks where overhead isn't wanted |
| Max File Size | 16 TiB | 8 EiB | Depends on filesystem on top |
| Max Volume Size | 1 EiB | 8 EiB | Partition table limit (2TB for MBR, 9.4ZB for GPT) |
| Online Shrink | Supported (unmount required) | Not supported — cannot shrink xfs volumes | Not applicable |
| Online Grow | Yes with resize2fs | Yes with xfs_growfs | Requires partition resize (risky, usually needs unmount) |
| Journaling | Yes (protects metadata on crash) | Yes (metadata-only by default) | N/A — filesystem-level feature |
| Inode Flexibility | Fixed at format time | Dynamic inode allocation (no inode exhaustion) | N/A |
| Recovery Tooling | e2fsck — mature, well-documented | xfs_repair — powerful but less forgiving | N/A |
| LVM Compatible | Yes — recommended pairing | Yes — recommended for large data volumes | No LVM layer — static allocation only |
| Cloud Usage (AWS/GCP) | Common for root volumes | Common for data volumes, EBS optimized workloads | Rarely used directly in cloud environments |
🎯 Key Takeaways
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Using /dev/sdX names in /etc/fstab instead of UUIDs — Symptom: server boots fine in the VM but after a hardware migration or adding a disk, the wrong filesystem mounts in the wrong place (or nothing mounts at all, dropping into emergency mode) — Fix: always use UUID from 'sudo blkid -s UUID -o value /dev/sdX1' in fstab. UUIDs are written into the filesystem metadata and follow the disk wherever it goes.
- ✕Mistake 2: Running lvextend without the -r flag — Symptom: lvextend succeeds and 'sudo lvs' shows the new size, but 'df -h' still shows the old size and the application can't use the extra space — Fix: either rerun with 'sudo lvextend -L +50G -r /dev/mapper/vg-lv' (the -r flag auto-resizes the filesystem), or manually run 'sudo resize2fs /dev/mapper/vg-lv' for ext4 or 'sudo xfs_growfs /mount/point' for xfs. Remember: the LV is the container; the filesystem inside it is a separate thing that also needs resizing.
- ✕Mistake 3: Formatting a partition that still has data on it — Symptom: 'mkfs.ext4 /dev/sdb1' completes instantly and silently overwrites everything that was on that partition — Fix: before any mkfs command, always run 'sudo mount | grep sdb1' to check if it's mounted, 'sudo lsblk /dev/sdb' to confirm what's on it, and 'sudo blkid /dev/sdb1' to see if it already has a filesystem. If you're on a server with multiple attached volumes, triple-check lsblk output before destructive operations. In a script, add a confirmation prompt or use a variable with a clearly wrong default (e.g., TARGET_DISK="SETME") so the script explodes safely if someone forgets to set it.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.