Skip to content

Storage & Filesystems

This page walks through the full storage stack: identifying disks, partitioning, creating filesystems, mounting them (temporarily and persistently), managing swap, and using LVM for flexible, resizable storage.

Tested on

AlmaLinux 9.4 (default filesystem XFS) and Ubuntu 22.04 LTS (default ext4). LVM and the partitioning tools behave identically across both.

These commands destroy data

Partitioning (fdisk, parted) and formatting (mkfs.*) erase whatever is on the target device. There is no undo. Always confirm the device name with lsblk first, and never operate on a disk you can't afford to wipe. In this guide /dev/sdb is an example empty disk — substitute your real device carefully.

Block devices

Disks and partitions are block devices under /dev (/dev/sda, /dev/nvme0n1, etc.).

# Tree view of disks, partitions, sizes, mountpoints, and filesystem types
lsblk -f
NAME        FSTYPE LABEL UUID                                 MOUNTPOINTS
sda
├─sda1      xfs          a1b2c3d4-...                         /boot
└─sda2      LVM2_member  e5f6...                              
  ├─vg0-root xfs         11aa...                              /
  └─vg0-swap swap        22bb...                              [SWAP]
sdb                                                           
# Detailed partition tables for all disks (needs root)
sudo fdisk -l

# Show the UUID, label, and filesystem type of devices
sudo blkid
sudo blkid /dev/sda1

Why UUIDs matter

Device names like /dev/sdb can change between boots (depending on detection order). UUIDs are stable identifiers tied to the filesystem — always use them in /etc/fstab.


Partitioning

A disk needs a partition table. Two formats:

  • MBR (msdos): legacy, max 2 TiB per disk, up to 4 primary partitions. Use only for very old/BIOS-only systems.
  • GPT: modern standard, supports huge disks and 128 partitions. Use GPT for anything new.

fdisk (interactive)

fdisk now handles both MBR and GPT.

sudo fdisk /dev/sdb

Inside fdisk, common commands: g (new GPT table), o (new MBR table), n (new partition), p (print), t (change type), d (delete), w (write and exit), q (quit without saving).

Command (m for help): g          # create a GPT label
Command (m for help): n          # new partition
Partition number (1-128, default 1): <Enter>
First sector ...: <Enter>
Last sector ...: +20G            # 20 GiB partition
Command (m for help): w          # commit

Nothing is written until you press w

fdisk only modifies the on-disk table when you type w. If you make a mistake, press q to bail out with no changes.

parted (scriptable)

parted can be driven non-interactively — handy for automation.

# Create a GPT table and a single partition spanning the whole disk
sudo parted -s /dev/sdb mklabel gpt
sudo parted -s /dev/sdb mkpart primary xfs 1MiB 100%

# Inspect
sudo parted /dev/sdb print

After partitioning, re-read the table if the kernel didn't pick it up:

sudo partprobe /dev/sdb
lsblk /dev/sdb

Creating filesystems

A partition is just space until you put a filesystem on it.

# XFS (RHEL/AlmaLinux default — great general-purpose choice)
sudo mkfs.xfs /dev/sdb1

# ext4 (Debian/Ubuntu default)
sudo mkfs.ext4 /dev/sdb1

# Add a label while formatting
sudo mkfs.xfs -L data /dev/sdb1
sudo mkfs.ext4 -L data /dev/sdb1

mkfs wipes the partition

mkfs.* creates a brand-new, empty filesystem, destroying any existing data on that partition. Triple-check the device path.

XFS cannot shrink

XFS can grow online but cannot be shrunk. ext4 can shrink (offline). If you expect to reduce a filesystem later, choose ext4 — or better, use LVM so you can manage space at the volume level.


Mounting and unmounting

Mounting attaches a filesystem to a directory (a mount point).

# Create a mount point and mount the filesystem there
sudo mkdir -p /mnt/data
sudo mount /dev/sdb1 /mnt/data

# Confirm
mount | grep /mnt/data
df -h /mnt/data

# Unmount (the directory must not be in use)
sudo umount /mnt/data
# If umount complains "target is busy", find what's using it
sudo lsof +D /mnt/data
sudo fuser -vm /mnt/data

A manual mount does not survive a reboot — for that you need /etc/fstab.


Persistent mounts: /etc/fstab

/etc/fstab tells the system what to mount at boot. Use UUIDs for reliability.

# Get the UUID of the filesystem
sudo blkid /dev/sdb1
/dev/sdb1: LABEL="data" UUID="9f8e7d6c-1234-5678-9abc-def012345678" TYPE="xfs"

Add a line to /etc/fstab:

# <device>                                  <mountpoint>  <type>  <options>        <dump> <fsck>
UUID=9f8e7d6c-1234-5678-9abc-def012345678   /mnt/data     xfs     defaults         0      0
# Validate the fstab and mount everything in it WITHOUT rebooting
sudo mount -a

# On systemd hosts, also refresh the mount units it generates
sudo systemctl daemon-reload

A broken fstab can block boot

A bad /etc/fstab entry can leave the system stuck at an emergency prompt on the next reboot. Always run sudo mount -a after editing — if it errors, fix it before you reboot. Adding the nofail option to non-critical mounts lets the system boot even if the device is missing.


Disk usage

# Free space per mounted filesystem (human-readable)
df -h

# Inode usage (a disk can be "full" of inodes while showing free space)
df -i

# Size of a directory (summarized, human-readable)
du -sh /var/log

# Biggest subdirectories, sorted
sudo du -h --max-depth=1 /var | sort -hr | head

Swap

Swap is disk space the kernel uses to offload memory under pressure. It can be a partition or a file.

# Show current swap
swapon --show
free -h

Create a swap file (the easy, flexible method):

# Create a 2 GiB file (fallocate is fast; on XFS use dd if fallocate misbehaves)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile

# Format it as swap and enable it
sudo mkswap /swapfile
sudo swapon /swapfile

# Make it persistent in /etc/fstab
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# Turn a swap device off
sudo swapoff /swapfile

swappiness

Control how aggressively the kernel swaps with vm.swappiness (0–100, default ~60). Lower it for memory-heavy servers: sudo sysctl vm.swappiness=10, and persist it in /etc/sysctl.d/99-swappiness.conf.


LVM basics

LVM (Logical Volume Manager) adds a flexible layer over physical disks so you can resize, combine, and snapshot storage without repartitioning. The stack:

  • PV (Physical Volume): a disk or partition handed to LVM.
  • VG (Volume Group): a pool made of one or more PVs.
  • LV (Logical Volume): a "virtual partition" carved from a VG — this is what you format and mount.
[ /dev/sdb1 ] [ /dev/sdc1 ]   →  PVs
        \        /
         [  vg_data  ]          →  VG (pooled space)
        /     |      \
   lv_web  lv_db   lv_logs      →  LVs (formatted & mounted)

Building an LVM stack

# 1. Initialize disks/partitions as physical volumes
sudo pvcreate /dev/sdb1 /dev/sdc1
sudo pvs                                   # list PVs

# 2. Create a volume group from them
sudo vgcreate vg_data /dev/sdb1 /dev/sdc1
sudo vgs                                   # list VGs

# 3. Create a 10 GiB logical volume
sudo lvcreate -L 10G -n lv_web vg_data
sudo lvs                                   # list LVs

# 4. Format and mount it (path is /dev/<vg>/<lv>)
sudo mkfs.xfs /dev/vg_data/lv_web
sudo mkdir -p /srv/web
sudo mount /dev/vg_data/lv_web /srv/web

Add to /etc/fstab using the LV's UUID (sudo blkid /dev/vg_data/lv_web) for persistence.

Extending a logical volume (online)

This is LVM's killer feature — grow a volume that's running out of space, live.

# Add more physical capacity to the VG first if needed
sudo pvcreate /dev/sdd1
sudo vgextend vg_data /dev/sdd1

# Grow the LV by 5 GiB (or use -l +100%FREE to use all remaining space)
sudo lvextend -L +5G /dev/vg_data/lv_web

# Now grow the FILESYSTEM to fill the larger LV:
#   XFS — must be MOUNTED, grows in place:
sudo xfs_growfs /srv/web

#   ext4 — works mounted or unmounted:
sudo resize2fs /dev/vg_data/lv_web

Combine lvextend + resize in one step

sudo lvextend -r -L +5G /dev/vg_data/lv_web resizes the filesystem automatically (-r/--resizefs), picking xfs_growfs or resize2fs for you.

Grow vs shrink

Growing is safe and online. Shrinking an LV requires shrinking the filesystem first and is risky (XFS can't shrink at all). Back up before any shrink operation.


Verify your work

# Confirm the device, filesystem type, and UUID
lsblk -f /dev/sdb
sudo blkid /dev/sdb1

# Confirm it is mounted with the right size and space
df -h /mnt/data

# Confirm fstab is valid (no errors = good)
sudo mount -a && echo "fstab OK"

# Confirm LVM layout
sudo pvs && sudo vgs && sudo lvs

# Confirm swap is active
swapon --show

A correctly configured volume shows up in lsblk -f with the expected type/UUID, appears in df -h at its mount point, and survives mount -a without errors.

Summary

  • Identify storage with lsblk -f, fdisk -l, and blkid; UUIDs are the stable way to reference filesystems.
  • Partition with fdisk (interactive) or parted (scriptable); prefer GPT over MBR for modern disks.
  • Create filesystems with mkfs.xfs (RHEL default, grow-only) or mkfs.ext4 (Debian default, can shrink).
  • Mount manually with mount/umount; make it permanent in /etc/fstab (always mount -a to test before rebooting).
  • Check space with df -h / df -i / du -sh; add swap via a swapfile (fallocatemkswapswapon → fstab).
  • LVM (PV → VG → LV) gives resizable storage: pvcreate/vgcreate/lvcreate, then lvextend plus xfs_growfs/resize2fs to grow online.

Test yourself