Disks, Partitions & Filesystems

Why This Matters

A monitoring alert fires: "Disk usage on /var has crossed 90%." You need to add a new disk, partition it, create a filesystem, and mount it -- all without rebooting, all without losing data, and ideally within the next fifteen minutes before the application crashes because it cannot write logs.

This is not a rare event. Disk management is a bread-and-butter skill for any Linux administrator. Whether you are provisioning a new server, expanding storage for a growing database, or preparing a fresh cloud instance, you need to understand how Linux sees physical disks, how you carve them into partitions, how you put a filesystem on top, and how you make that filesystem accessible to the rest of the system.


Try This Right Now

# What block devices does the system have?
lsblk

# How much disk space is used?
df -h

# What filesystems are mounted?
mount | column -t | head -20

# What is the disk usage of your current directory?
du -sh .

# What are the UUIDs of your partitions?
sudo blkid

These five commands form the core toolkit for day-to-day disk management. Let us understand every one of them deeply.


Block Devices: How Linux Sees Disks

A block device is a piece of hardware (or virtualized hardware) that stores data in fixed-size blocks and supports random access. Hard drives, SSDs, NVMe drives, USB sticks, and virtual disks are all block devices.

Linux represents block devices as files in /dev/:

Device type       Naming scheme         Example
-----------       -------------         -------
SATA/SCSI/SAS     /dev/sdX              /dev/sda, /dev/sdb
NVMe              /dev/nvmeXnY          /dev/nvme0n1
VirtIO (VMs)      /dev/vdX              /dev/vda, /dev/vdb
MMC/SD cards      /dev/mmcblkX          /dev/mmcblk0
Floppy (legacy)   /dev/fdX              /dev/fd0

Partitions are numbered:

/dev/sda          <-- Entire first SATA disk
/dev/sda1         <-- First partition on sda
/dev/sda2         <-- Second partition on sda
/dev/sda3         <-- Third partition on sda

/dev/nvme0n1      <-- Entire first NVMe drive
/dev/nvme0n1p1    <-- First partition on nvme0n1
/dev/nvme0n1p2    <-- Second partition on nvme0n1

lsblk -- Your Best Friend

lsblk lists all block devices in a tree format, showing the relationship between disks, partitions, and mount points.

lsblk

Typical output:

NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0    50G  0 disk
├─sda1        8:1    0     1G  0 part /boot
├─sda2        8:2    0    45G  0 part /
└─sda3        8:3    0     4G  0 part [SWAP]
sdb           8:16   0   100G  0 disk
└─sdb1        8:17   0   100G  0 part /data

Useful lsblk options:

# Show filesystem type and UUIDs
lsblk -f

# Show sizes in bytes
lsblk -b

# Show all devices including empty ones
lsblk -a

# Output specific columns
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID

MBR vs GPT: Partition Table Formats

Before you create partitions, the disk needs a partition table -- a data structure at the beginning of the disk that records the layout.

MBR (Master Boot Record)

  • The original PC partition scheme from 1983
  • Supports up to 4 primary partitions (or 3 primary + 1 extended with unlimited logical partitions)
  • Maximum disk size: 2 TB
  • Stores the partition table in the first 512 bytes of the disk
  • Used with BIOS boot

GPT (GUID Partition Table)

  • Modern standard, part of UEFI specification
  • Supports up to 128 partitions by default (no extended/logical nonsense)
  • Maximum disk size: 9.4 ZB (zettabytes -- effectively unlimited)
  • Stores redundant copies of the partition table (beginning and end of disk)
  • Includes CRC32 checksums for integrity
  • Used with UEFI boot (but can also work with BIOS via "BIOS boot partition")
+-------------------------------------------------------------------+
|                     MBR vs GPT Comparison                         |
+-------------------------------------------------------------------+
|                                                                     |
|   MBR (Legacy)              GPT (Modern)                          |
|   +-----------+             +--+--+--+--+--+--+--+--+--+--+      |
|   | MBR | P1 | P2 | P3 | P4 |   | GP | P1 | P2 | .. | Pn | GP |  |
|   +-----------+             +--+--+--+--+--+--+--+--+--+--+      |
|   Max 4 primary             Up to 128 partitions                   |
|   Max 2 TB disk             Max 9.4 ZB disk                       |
|   No redundancy             Backup table at disk end              |
|   BIOS boot                 UEFI boot (usually)                   |
|                                                                     |
+-------------------------------------------------------------------+

Think About It: When should you still use MBR? Mainly for legacy systems that boot with BIOS (not UEFI), or for very small disks where compatibility with older tools matters. For anything new, use GPT.


Partitioning Tools: fdisk and parted

fdisk -- Interactive Disk Partitioning

fdisk is the classic Linux partitioning tool. It now supports both MBR and GPT. It is interactive but straightforward.

Safety Warning: Partitioning a disk that contains data will destroy that data. Always double-check the device name. Running fdisk /dev/sda when you meant /dev/sdb could wipe your operating system. Use lsblk first to confirm which disk is which.

# ALWAYS verify your target disk first
lsblk

# View existing partition table (safe, read-only)
sudo fdisk -l /dev/sdb

# Start interactive partitioning (ONLY on a disk you want to modify)
sudo fdisk /dev/sdb

Inside fdisk, the key commands are:

CommandAction
pPrint current partition table
nCreate a new partition
dDelete a partition
tChange partition type
gCreate a new GPT partition table
oCreate a new MBR partition table
wWrite changes and exit
qQuit without saving

Hands-On: Partitioning with fdisk (Using a Loop Device)

We will safely practice by creating a virtual disk using a loop device. This will not touch your real disks.

# Create a 500MB file to use as a virtual disk
dd if=/dev/zero of=/tmp/practice-disk.img bs=1M count=500

# Attach it as a loop device
sudo losetup -fP /tmp/practice-disk.img
LOOPDEV=$(sudo losetup -j /tmp/practice-disk.img | cut -d: -f1)
echo "Our virtual disk is: $LOOPDEV"

# Verify it appears
lsblk $LOOPDEV

# Now partition it with fdisk
sudo fdisk $LOOPDEV

Inside fdisk, follow these steps:

Command (m for help): g        <-- Create GPT partition table
Command (m for help): n        <-- New partition
Partition number: 1            <-- Accept default
First sector: [Enter]          <-- Accept default
Last sector: +200M             <-- 200MB partition
Command (m for help): n        <-- New partition
Partition number: 2            <-- Accept default
First sector: [Enter]          <-- Accept default
Last sector: [Enter]           <-- Use remaining space
Command (m for help): p        <-- Print to verify
Command (m for help): w        <-- Write and exit
# Verify the partitions
lsblk $LOOPDEV

# You should see:
# loop0        7:0    0   500M  0 loop
# ├─loop0p1  259:0    0   200M  0 part
# └─loop0p2  259:1    0   298M  0 part

parted -- Non-Interactive Partitioning

parted is more powerful than fdisk and can be used non-interactively, which makes it better for scripting.

# View disk info
sudo parted $LOOPDEV print

# Non-interactive examples (DON'T run these -- just reference):
# Create GPT label
# sudo parted /dev/sdb mklabel gpt

# Create a partition from 1MiB to 500MiB
# sudo parted /dev/sdb mkpart primary ext4 1MiB 500MiB

Distro Note: On Fedora/RHEL, parted is installed by default. On minimal Debian/Ubuntu installs, you may need sudo apt install parted.


Creating Filesystems with mkfs

A partition is just a raw chunk of disk space. Before you can store files on it, you need to put a filesystem on it. The mkfs command creates filesystems.

Common Linux Filesystems

FilesystemCommandStrengthsUse Case
ext4mkfs.ext4Mature, reliable, widely supportedGeneral purpose, default choice
XFSmkfs.xfsExcellent large file / parallel I/ODatabases, large files, RHEL default
Btrfsmkfs.btrfsSnapshots, checksums, compressionAdvanced features, openSUSE/Fedora
# Create ext4 filesystem on our practice partitions
sudo mkfs.ext4 ${LOOPDEV}p1

# Create XFS filesystem
sudo mkfs.xfs ${LOOPDEV}p2

# Verify
sudo blkid ${LOOPDEV}p1 ${LOOPDEV}p2
lsblk -f $LOOPDEV

You should see output like:

NAME       FSTYPE LABEL UUID                                 MOUNTPOINT
loop0
├─loop0p1  ext4         a1b2c3d4-e5f6-7890-abcd-ef1234567890
└─loop0p2  xfs          f1e2d3c4-b5a6-7890-abcd-ef0987654321

mkfs Options

# ext4 with a label
sudo mkfs.ext4 -L "mydata" /dev/sdXn

# ext4 with specific block size
sudo mkfs.ext4 -b 4096 /dev/sdXn

# XFS with a label
sudo mkfs.xfs -L "bigdata" /dev/sdXn

# Btrfs with a label
sudo mkfs.btrfs -L "snapshots" /dev/sdXn

Safety Warning: mkfs destroys all existing data on the partition. There is no "Are you sure?" prompt for most variants. Triple-check the device name before running it.


Mounting and Unmounting Filesystems

A filesystem on a partition is useless until it is mounted -- attached to a directory in the filesystem tree.

mount -- Attach a Filesystem

# Create mount points
sudo mkdir -p /mnt/part1
sudo mkdir -p /mnt/part2

# Mount our practice partitions
sudo mount ${LOOPDEV}p1 /mnt/part1
sudo mount ${LOOPDEV}p2 /mnt/part2

# Verify
df -h /mnt/part1 /mnt/part2
mount | grep loop

# Use the filesystems
echo "Hello from ext4!" | sudo tee /mnt/part1/hello.txt
echo "Hello from XFS!" | sudo tee /mnt/part2/hello.txt
cat /mnt/part1/hello.txt
cat /mnt/part2/hello.txt

umount -- Detach a Filesystem

# Unmount (note: it's "umount", not "unmount"!)
sudo umount /mnt/part1
sudo umount /mnt/part2

If you get "device is busy":

# Find what is using the mount point
sudo lsof +D /mnt/part1
# or
sudo fuser -mv /mnt/part1

# Force unmount (use with caution)
sudo umount -l /mnt/part1    # Lazy unmount -- detaches immediately,
                              # cleans up when no longer in use

Mount Options

# Mount read-only
sudo mount -o ro ${LOOPDEV}p1 /mnt/part1

# Mount with no-exec (prevent running executables)
sudo mount -o noexec ${LOOPDEV}p1 /mnt/part1

# Mount with nosuid (ignore SUID bits)
sudo mount -o nosuid ${LOOPDEV}p1 /mnt/part1

# Combine options
sudo mount -o ro,noexec,nosuid ${LOOPDEV}p1 /mnt/part1

# Remount with different options (without unmounting)
sudo mount -o remount,rw /mnt/part1

Persistent Mounts with /etc/fstab

Mounts created with the mount command are temporary -- they disappear on reboot. To make mounts persistent, add them to /etc/fstab.

# View your current fstab
cat /etc/fstab

fstab Format

Each line in /etc/fstab has six fields:

<device>     <mount point>  <fstype>  <options>       <dump>  <pass>
UUID=xxxx    /data          ext4      defaults        0       2
/dev/sdb1    /backup        xfs       defaults,noatime 0      2
FieldMeaning
deviceWhat to mount (UUID, LABEL, or device path)
mount pointWhere to mount it
fstypeFilesystem type (ext4, xfs, btrfs, swap, etc.)
optionsMount options (defaults, ro, noexec, noatime, etc.)
dumpBackup with dump? (0=no, 1=yes. Almost always 0.)
passfsck order (0=skip, 1=root, 2=other filesystems)

Using UUIDs (Best Practice)

Device names like /dev/sdb1 can change if you add or remove disks. UUIDs are unique identifiers that never change.

# Find UUIDs
sudo blkid

# Example output:
# /dev/sda1: UUID="a1b2c3d4-..." TYPE="ext4"
# /dev/sda2: UUID="e5f6a7b8-..." TYPE="swap"

Adding an Entry to fstab

# Get the UUID of your partition
UUID=$(sudo blkid -s UUID -o value ${LOOPDEV}p1)
echo "UUID is: $UUID"

# Add to fstab (be VERY careful editing this file)
echo "UUID=$UUID  /mnt/part1  ext4  defaults  0  2" | sudo tee -a /etc/fstab

# Test without rebooting
sudo mount -a

# Verify
df -h /mnt/part1

Safety Warning: A syntax error in /etc/fstab can prevent your system from booting. Always test with sudo mount -a after editing. If you are editing remotely, keep a second SSH session open as a safety net. Also consider using sudo findmnt --verify to check for errors.

# Validate fstab syntax
sudo findmnt --verify --tab-file /etc/fstab

Think About It: Why does the pass field matter? Think about what happens if the system loses power while writing to disk, and the filesystem needs to be checked for consistency before it can be safely used.


Checking Disk Space: df and du

df -- Disk Free (Filesystem Level)

Shows how much space is used and available on each mounted filesystem.

# Human-readable sizes
df -h

# Show filesystem types
df -hT

# Check specific mount point
df -h /var

# Show only real filesystems (exclude tmpfs, devtmpfs, etc.)
df -h -x tmpfs -x devtmpfs -x squashfs

Example output:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        45G   12G   31G  28% /
/dev/sda1       976M  150M  760M  17% /boot
/dev/sdb1       100G   45G   55G  45% /data

du -- Disk Usage (Directory Level)

Shows the disk space used by files and directories.

# Size of current directory
du -sh .

# Size of each subdirectory
du -sh /var/*/ 2>/dev/null | sort -rh | head -10

# Size of a specific directory, 1 level deep
du -h --max-depth=1 /var 2>/dev/null | sort -rh

# Find the largest files in a directory
du -ah /var/log 2>/dev/null | sort -rh | head -10

# Total size of a directory
du -sh /usr

Practical: Finding What Is Eating Your Disk

# Step 1: Which filesystem is full?
df -h

# Step 2: Find the biggest directories
sudo du -h --max-depth=1 / 2>/dev/null | sort -rh | head -10

# Step 3: Drill down into the culprit
sudo du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -10

# Step 4: Find individual large files
sudo find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -10

# Step 5: Check for deleted files still held open
sudo lsof +L1 2>/dev/null

Swap Space

Swap is disk space used as an extension of RAM. When physical memory is full, the kernel moves inactive pages to swap. While slower than RAM, it prevents out-of-memory crashes.

Checking Current Swap

# View swap
free -h
swapon --show
cat /proc/swaps

Creating a Swap Partition

# On a real disk (example -- do not run blindly):
# sudo mkswap /dev/sda3
# sudo swapon /dev/sda3

Creating a Swap File

This is often easier than creating a swap partition, especially on cloud instances.

# Create a 1GB swap file
sudo fallocate -l 1G /swapfile
# or: sudo dd if=/dev/zero of=/swapfile bs=1M count=1024

# Set correct permissions (MUST be 600)
sudo chmod 600 /swapfile

# Format as swap
sudo mkswap /swapfile

# Enable it
sudo swapon /swapfile

# Verify
swapon --show
free -h

To make it permanent, add to /etc/fstab:

/swapfile    none    swap    sw    0    0

Swappiness

The vm.swappiness parameter controls how aggressively the kernel uses swap (0-100).

# Check current value (default is usually 60)
cat /proc/sys/vm/swappiness

# Reduce swappiness (prefer RAM over swap)
sudo sysctl vm.swappiness=10

# Make permanent
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.d/99-swappiness.conf

Hands-On: Complete Disk Workflow

Let us bring everything together. We will use our practice loop device to walk through the full lifecycle.

# Step 1: Create a virtual disk
dd if=/dev/zero of=/tmp/lab-disk.img bs=1M count=200

# Step 2: Attach as loop device
sudo losetup -fP /tmp/lab-disk.img
DISK=$(sudo losetup -j /tmp/lab-disk.img | cut -d: -f1)
echo "Working with: $DISK"

# Step 3: Create a GPT partition table and one partition
echo -e "g\nn\n1\n\n\nw" | sudo fdisk $DISK

# Step 4: Create an ext4 filesystem with a label
sudo mkfs.ext4 -L "labdata" ${DISK}p1

# Step 5: Create a mount point
sudo mkdir -p /mnt/labdata

# Step 6: Mount it
sudo mount ${DISK}p1 /mnt/labdata

# Step 7: Verify
lsblk $DISK
df -h /mnt/labdata
mount | grep labdata

# Step 8: Use it
echo "Disk management is easy!" | sudo tee /mnt/labdata/test.txt
cat /mnt/labdata/test.txt

# Step 9: Check the filesystem details
sudo tune2fs -l ${DISK}p1 | head -20    # ext4 info
sudo dumpe2fs ${DISK}p1 2>/dev/null | head -30

# Step 10: Clean up
sudo umount /mnt/labdata
sudo losetup -d $DISK
rm /tmp/lab-disk.img

Debug This

A developer reports: "I added a 100GB disk to the server and added it to /etc/fstab, but after rebooting the server, it boots into emergency mode." You check the console and see filesystem mount errors.

What are the likely causes?

Solution

Common causes of fstab-related boot failures:

  1. Typo in UUID. Even one wrong character will prevent the mount from finding the device.

    sudo blkid  # Verify the correct UUID
    
  2. Wrong filesystem type. If the fstab says ext4 but the partition has xfs, the mount fails.

    sudo blkid -s TYPE -o value /dev/sdb1
    
  3. Device name changed. If fstab uses /dev/sdb1 and the disk enumeration changed, the mount fails. This is why UUIDs are recommended.

  4. Missing mount point directory. If the directory in fstab does not exist, the mount fails.

    ls -ld /mnt/data
    
  5. The pass (fsck) field. If set to a non-zero value and the filesystem check fails (corrupted filesystem), the system drops to emergency mode.

Fix from emergency mode:

# Edit fstab to fix the error
vi /etc/fstab

# Or use nofail option to prevent boot failure
UUID=xxxx  /data  ext4  defaults,nofail  0  2

The nofail mount option tells the system to continue booting even if this mount fails -- critical for non-essential data partitions.


What Just Happened?

+------------------------------------------------------------------+
|                    CHAPTER 7 RECAP                                |
+------------------------------------------------------------------+
|                                                                    |
|  Block Devices:                                                    |
|    /dev/sdX (SATA)  /dev/nvmeXnY (NVMe)  /dev/vdX (VirtIO)     |
|                                                                    |
|  Partition Tables: MBR (legacy, 2TB max) vs GPT (modern)         |
|                                                                    |
|  Partitioning:  fdisk (interactive)  parted (scriptable)         |
|                                                                    |
|  Filesystems: ext4 (default), XFS (large), Btrfs (snapshots)    |
|  Create with: mkfs.ext4, mkfs.xfs, mkfs.btrfs                   |
|                                                                    |
|  Mounting: mount/umount, /etc/fstab for persistence              |
|  Always use UUIDs in fstab, not device names                     |
|  Use 'nofail' for non-critical mounts                            |
|                                                                    |
|  Space checks: df -h (filesystem), du -sh (directory)            |
|                                                                    |
|  Swap: mkswap + swapon, swap files for flexibility               |
|                                                                    |
|  Tools: lsblk, blkid, fdisk, parted, mkfs, mount, umount,       |
|         df, du, swapon, free, findmnt                            |
|                                                                    |
+------------------------------------------------------------------+

Try This

Exercises

  1. Explore your system. Run lsblk -f and df -hT. Identify every physical disk, partition, filesystem type, and mount point on your system. Draw it as a tree diagram.

  2. Practice safe partitioning. Create a 1GB virtual disk with dd, partition it into three parts using fdisk (a 200MB ext4, a 500MB XFS, and the rest as swap). Create the filesystems, mount them, and verify.

  3. fstab lab. For each partition in exercise 2, add an fstab entry using UUIDs. Use the nofail option. Test with sudo mount -a.

  4. Disk usage investigation. Write a one-liner that finds the 10 largest files on your system (hint: find / -type f -printf '%s %p\n' | sort -rn | head -10).

  5. Swap file. Create a 512MB swap file, enable it, verify it shows in free -h, then disable and remove it.

Bonus Challenge

Write a script called disk-report.sh that generates a comprehensive disk report:

#!/bin/bash
echo "=== Disk Report: $(hostname) -- $(date) ==="
echo ""

echo "--- Block Devices ---"
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID

echo ""
echo "--- Filesystem Usage ---"
df -hT -x tmpfs -x devtmpfs -x squashfs

echo ""
echo "--- Largest Directories Under / ---"
sudo du -h --max-depth=1 / 2>/dev/null | sort -rh | head -10

echo ""
echo "--- Swap Status ---"
free -h | grep -i swap
swapon --show

echo ""
echo "--- Partitions Over 80% Full ---"
df -h | awk 'NR>1 && int($5)>80 {print $0}'

echo ""
echo "--- fstab Entries ---"
grep -v '^#' /etc/fstab | grep -v '^$'

Next up: Chapter 8 -- Links, Inodes & the VFS. You now understand the physical storage layer. It is time to peek behind the curtain and see how Linux actually tracks files internally.