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.
A raw disk or partition initialized for LVM use.
The bottom layer â actual physical storage.
Commands: pvcreate, pvdisplay, pvs
A pool of storage built from one or more PVs.
The middle layer â storage abstracted from physical devices.
Commands: vgcreate, vgdisplay, vgs
A slice of a VG â analogous to a partition.
Formatted with a filesystem and mounted.
Commands: lvcreate, lvdisplay, lvs
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/sdbpvs output:
/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.
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_datavgs output:
vg_ followed by the purpose â vg_data,
vg_apps, vg_backup. Makes administration
much cleaner when you have multiple volume groups.
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_homelvs output:
/dev/vg_data/lv_home (symlink â easier to read)/dev/mapper/vg_data-lv_home (actual device mapper name)
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
/dev/mapper/ names or UUIDs for LVM volumes â it is
more readable and just as reliable since the symlink is maintained
by LVM itself.
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 UUIDlsblk output showing LVM structure:
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
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
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. The command
will prompt for confirmation â read it carefully.
| Command | What it does |
|---|---|
| pvcreate /dev/sdX | Initialize disk as Physical Volume |
| pvs | List all PVs â summary |
| pvdisplay /dev/sdX | Detailed PV information |
| vgcreate vgname /dev/sdX | Create Volume Group from PV(s) |
| vgs | List all VGs â summary |
| vgdisplay vgname | Detailed VG information |
| vgextend vgname /dev/sdX | Add a PV to an existing VG |
| lvcreate -L 100G -n lvname vgname | Create 100GB Logical Volume |
| lvcreate -l 100%FREE -n lvname vgname | Use all remaining VG space |
| lvs | List all LVs â summary |
| lvdisplay /dev/vgname/lvname | Detailed LV information |
| mkfs.xfs /dev/vgname/lvname | Format LV with XFS |
| mkfs.ext4 /dev/vgname/lvname | Format LV with ext4 |
| lvremove /dev/vgname/lvname | Remove an LV (destructive) |
| vgremove vgname | Remove a VG |
| pvremove /dev/sdX | Remove PV label from disk |
| lsblk | Show block device tree including LVM |
| Object | Convention | Examples |
|---|---|---|
| Volume Group | vg_ prefix + purpose | vg_data, vg_apps, vg_backup |
| Logical Volume | lv_ prefix + mountpoint | lv_home, lv_var, lv_opt |
| Device path | /dev/vgname/lvname | /dev/vg_data/lv_home |