💾 LVM Basics

Storage Series: Part 1 — LVM Basics  |  Part 2 — LVM Management  |  Part 3 — LVM Snapshots  |  Part 4 — RAID Overview

What is LVM?

Logical Volume Manager (LVM) is an abstraction layer between physical storage and the filesystems that use it. Instead of partitioning disks directly, LVM lets you create flexible storage pools that can span multiple physical disks, be resized online, snapshotted, and managed without rebooting.

LVM is standard on virtually every RHEL/CentOS/Rocky installation and is the foundation for features like thin provisioning, snapshots, and online resize. Understanding PVs, VGs, and LVs is essential for any Linux sysadmin managing server storage.

The Three LVM Layers

PV

Physical Volume

A raw disk or partition initialized for LVM use. The bottom layer — actual physical storage. Commands: pvcreate, pvdisplay, pvs

VG

Volume Group

A pool of storage built from one or more PVs. The middle layer — storage abstracted from physical devices. Commands: vgcreate, vgdisplay, vgs

LV

Logical Volume

A slice of a VG — analogous to a partition. Formatted with a filesystem and mounted. Commands: lvcreate, lvdisplay, lvs

[ /dev/vg_data/lv_home ] [ /dev/vg_data/lv_var ] [ /dev/vg_data/lv_opt ] Logical Volumes (LVs) ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ [ vg_data — 2.7 TiB pool ] Volume Group (VG) ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ [ /dev/sdb — PV ] [ /dev/sdc — PV ] [ /dev/sdd — PV ] Physical Volumes (PVs) ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ [ 900GB disk ] [ 900GB disk ] [ 900GB disk ] Physical Disks

Examples

1
Prepare a Disk — pvcreate

Before a disk can join an LVM volume group it must be initialized as a Physical Volume:

# First — identify available disks
lsblk
fdisk -l | grep "^Disk /dev"

# Initialize a whole disk as a PV (most common — no partitioning needed)
sudo pvcreate /dev/sdb
sudo pvcreate /dev/sdc
sudo pvcreate /dev/sdd

# Initialize multiple PVs at once
sudo pvcreate /dev/sd{b,c,d}

# Initialize a partition (rather than a whole disk)
sudo pvcreate /dev/sdb1

# Verify
sudo pvs
sudo pvdisplay /dev/sdb
pvs output:
PV VG Fmt Attr PSize PFree /dev/sdb lvm2 --- 900.00g 900.00g /dev/sdc lvm2 --- 900.00g 900.00g /dev/sdd lvm2 --- 900.00g 900.00g
Whole disk vs partition: Using whole disks (/dev/sdb rather than /dev/sdb1) is the modern best practice for LVM. It avoids partition table overhead and is simpler to manage. Only use partitions when mixing LVM with non-LVM use on the same disk.
2
Create a Volume Group — vgcreate

A Volume Group pools one or more PVs into a single storage resource:

# Create a VG named vg_data from three PVs
sudo vgcreate vg_data /dev/sdb /dev/sdc /dev/sdd

# Create with a specific physical extent size (default is 4MB)
sudo vgcreate -s 16M vg_data /dev/sdb /dev/sdc

# Verify
sudo vgs
sudo vgdisplay vg_data
vgs output:
VG #PV #LV #SN Attr VSize VFree vg_data 3 0 0 wz--n- 2.70t 2.70t
vgdisplay vg_data output (abbreviated):
--- Volume group --- VG Name vg_data System ID Format lvm2 VG Size 2.70 TiB PE Size 4.00 MiB Total PE 708608 Alloc PE / Size 0 / 0 Free PE / Size 708608 / 2.70 TiB VG UUID xYzAbC-1234-...
💡 Naming convention: Use a descriptive prefix like vg_ followed by the purpose — vg_data, vg_apps, vg_backup. Makes administration much cleaner when you have multiple volume groups.
3
Create Logical Volumes — lvcreate

Logical Volumes are carved from the VG pool — each becomes a block device you can format and mount:

# Create an LV of a specific size
sudo lvcreate -L 100G -n lv_home vg_data
sudo lvcreate -L 200G -n lv_var  vg_data
sudo lvcreate -L 50G  -n lv_opt  vg_data

# Create an LV using a percentage of free space
sudo lvcreate -l 50%FREE -n lv_scratch vg_data

# Create an LV using ALL remaining free space
sudo lvcreate -l 100%FREE -n lv_data vg_data

# Verify
sudo lvs
sudo lvdisplay vg_data/lv_home
lvs output:
LV VG Attr LSize lv_home vg_data -wi-a----- 100.00g lv_var vg_data -wi-a----- 200.00g lv_opt vg_data -wi-a----- 50.00g
LV device paths: Each LV appears as a block device at two equivalent paths:
/dev/vg_data/lv_home (symlink — easier to read)
/dev/mapper/vg_data-lv_home (actual device mapper name)
4
Format and Mount Logical Volumes

An LV is just a block device — format it and mount it like any partition:

# Format with ext4
sudo mkfs.ext4 /dev/vg_data/lv_home
sudo mkfs.ext4 /dev/vg_data/lv_var

# Format with XFS (default on RHEL 8/9)
sudo mkfs.xfs /dev/vg_data/lv_opt

# Create mount points and mount
sudo mkdir -p /home /var/opt
sudo mount /dev/vg_data/lv_home /home
sudo mount /dev/vg_data/lv_var  /var
sudo mount /dev/vg_data/lv_opt  /opt

# Add to /etc/fstab for persistent mounts
echo "/dev/vg_data/lv_home  /home  ext4  defaults  0 2" | sudo tee -a /etc/fstab
echo "/dev/vg_data/lv_var   /var   ext4  defaults  0 2" | sudo tee -a /etc/fstab
echo "/dev/vg_data/lv_opt   /opt   xfs   defaults  0 2" | sudo tee -a /etc/fstab

# Verify mounts
df -h | grep vg_data
lsblk -f
💡 Use /dev/vg_name/lv_name in fstab rather than /dev/mapper/ names or UUIDs for LVM volumes — it is more readable and just as reliable since the symlink is maintained by LVM itself.
5
Display Commands — pvs, vgs, lvs

The short display commands give quick status — use them constantly:

# Summary views — most useful day-to-day
sudo pvs    # all physical volumes
sudo vgs    # all volume groups
sudo lvs    # all logical volumes

# Verbose detail
sudo pvdisplay
sudo vgdisplay
sudo lvdisplay

# Specific object
sudo pvdisplay /dev/sdb
sudo vgdisplay vg_data
sudo lvdisplay /dev/vg_data/lv_home

# Extended output with segments
sudo pvs -v
sudo lvs -o +devices

# Full picture — lsblk shows the tree
lsblk
lsblk -f   # includes filesystem type and UUID
lsblk output showing LVM structure:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sdb 8:16 0 900G 0 disk └─vg_data-lv_home 253:0 0 100G 0 lvm /home sdc 8:32 0 900G 0 disk └─vg_data-lv_var 253:1 0 200G 0 lvm /var sdd 8:48 0 900G 0 disk └─vg_data-lv_opt 253:2 0 50G 0 lvm /opt
6
Complete From-Scratch LVM Setup

The full workflow — new disks to mounted filesystems in one sequence:

#!/bin/bash
# New LVM setup — two 500GB disks → one VG → two LVs

DISK1=/dev/sdb
DISK2=/dev/sdc
VG=vg_apps
LV1=lv_appdata
LV2=lv_applogs

# Step 1: Initialize PVs
sudo pvcreate $DISK1 $DISK2

# Step 2: Create VG
sudo vgcreate $VG $DISK1 $DISK2

# Step 3: Create LVs
sudo lvcreate -L 800G -n $LV1 $VG
sudo lvcreate -l 100%FREE -n $LV2 $VG

# Step 4: Format
sudo mkfs.xfs /dev/$VG/$LV1
sudo mkfs.xfs /dev/$VG/$LV2

# Step 5: Mount
sudo mkdir -p /opt/appdata /var/log/apps
sudo mount /dev/$VG/$LV1 /opt/appdata
sudo mount /dev/$VG/$LV2 /var/log/apps

# Step 6: fstab
echo "/dev/$VG/$LV1  /opt/appdata    xfs  defaults  0 2" | sudo tee -a /etc/fstab
echo "/dev/$VG/$LV2  /var/log/apps   xfs  defaults  0 2" | sudo tee -a /etc/fstab

# Step 7: Verify
sudo pvs && sudo vgs && sudo lvs
df -h /opt/appdata /var/log/apps
7
LVM on a VM — Adding a New Virtual Disk

The most common real-world scenario — a VM gets a new virtual disk and you need to add it to an existing volume group:

# After adding disk in hypervisor — rescan for new device
sudo echo "- - -" > /sys/class/scsi_host/host0/scan
lsblk   # confirm new disk appears (e.g., /dev/sde)

# Initialize as PV
sudo pvcreate /dev/sde

# Add to existing VG
sudo vgextend vg_data /dev/sde

# Verify VG now has more free space
sudo vgs

# Extend an existing LV to use the new space
sudo lvextend -L +200G /dev/vg_data/lv_home

# Grow the filesystem (ext4)
sudo resize2fs /dev/vg_data/lv_home

# Grow the filesystem (XFS — must be mounted)
sudo xfs_growfs /home

# Verify
df -h /home
💡 This is LVM's killer feature. Adding storage to a running system with no downtime — extend the VG, extend the LV, grow the filesystem. Three commands, zero reboots, zero service interruption. This is why LVM is standard on production servers.
8
Remove LVM Objects Safely

Removal must be done in reverse order — LV first, then VG, then PV:

# Step 1: Unmount the filesystem
sudo umount /opt/appdata

# Step 2: Remove from fstab
sudo vi /etc/fstab   # remove the relevant line

# Step 3: Remove the LV
sudo lvremove /dev/vg_apps/lv_appdata
# Confirm with 'y' when prompted

# Step 4: Remove the VG (only if removing all LVs)
sudo vgremove vg_apps

# Step 5: Remove the PV label
sudo pvremove /dev/sdb

# Verify everything is gone
sudo pvs
sudo vgs
sudo lvs
⚠️ lvremove destroys data immediately. There is no recycle bin. Unmount first, double-check the LV name, and make sure you have a backup before running lvremove. The command will prompt for confirmation — read it carefully.

Quick Reference

CommandWhat it does
pvcreate /dev/sdXInitialize disk as Physical Volume
pvsList all PVs — summary
pvdisplay /dev/sdXDetailed PV information
vgcreate vgname /dev/sdXCreate Volume Group from PV(s)
vgsList all VGs — summary
vgdisplay vgnameDetailed VG information
vgextend vgname /dev/sdXAdd a PV to an existing VG
lvcreate -L 100G -n lvname vgnameCreate 100GB Logical Volume
lvcreate -l 100%FREE -n lvname vgnameUse all remaining VG space
lvsList all LVs — summary
lvdisplay /dev/vgname/lvnameDetailed LV information
mkfs.xfs /dev/vgname/lvnameFormat LV with XFS
mkfs.ext4 /dev/vgname/lvnameFormat LV with ext4
lvremove /dev/vgname/lvnameRemove an LV (destructive)
vgremove vgnameRemove a VG
pvremove /dev/sdXRemove PV label from disk
lsblkShow block device tree including LVM

LVM Object Naming Conventions

ObjectConventionExamples
Volume Groupvg_ prefix + purposevg_data, vg_apps, vg_backup
Logical Volumelv_ prefix + mountpointlv_home, lv_var, lv_opt
Device path/dev/vgname/lvname/dev/vg_data/lv_home

← Back to Storage Index ↑ Back to EXPANDED