Linux LVM (Logical Volume Management): Complete Step-by-Step Guide

LVM (Logical Volume Management) is one of the most powerful storage management features in Linux. It abstracts physical storage into flexible logical volumes that can be resized, moved, and snapshotted without system downtime. For RHCA, LVM mastery is non-negotiable — it is used in nearly every enterprise Linux deployment.

The Core Problem LVM Solves

Traditional partitions are inflexible. Once created, a partition's size is fixed by its physical location on the disk. To extend a full partition you must:

  1. Back up all data from the partition
  2. Unmount and delete the partition
  3. Recreate it with larger size (requires adjacent free space)
  4. Format and restore data

This process requires downtime, risks data loss, and may be impossible if adjacent space is occupied by another partition. LVM eliminates all of these constraints.

LVM Architecture — The Three Layers

LVM inserts an abstraction layer between physical disks and the file system. This layer has three tiers:

Layer 1: Physical Volumes (PV)

A Physical Volume is a block device (entire disk or a partition) that has been initialised for use by LVM. LVM writes a small header to the start of the PV containing metadata about the PV and the Volume Group it belongs to. Each PV is divided into fixed-size chunks called Physical Extents (PE). The default PE size is 4 MB.

  • PVs can be entire disks (/dev/sdb) or partitions (/dev/sdb1)
  • A partition intended for LVM should have type code 8e (Linux LVM)
  • The PV metadata includes the VG name, UUID, and PE size

Layer 2: Volume Groups (VG)

A Volume Group is a pool of storage formed by combining one or more Physical Volumes. The VG hides the individual disk boundaries — from the VG's perspective, storage is a single large pool of extents.

  • A VG can span multiple physical disks
  • Adding a new PV to a VG instantly increases the pool's capacity
  • VG metadata is replicated on every PV in the group (redundancy)
  • VG attributes: w = writable, z = extendable, n = normal allocation

Layer 3: Logical Volumes (LV)

A Logical Volume is carved out of the VG's pool of extents. It looks exactly like a regular partition to the file system. LVs are composed of Logical Extents (LE), each of which maps to a specific PE on a specific PV. This mapping is maintained in the VG metadata and is what enables LVs to be extended, reduced, moved, and snapshotted.

  • An LV can span multiple PVs transparently
  • LVs can be resized while mounted (grow) or after unmounting (shrink)
  • LVs appear as /dev/vgname/lvname or /dev/mapper/vgname-lvname

Physical Extents — The Unit of Allocation

The PE size is set when the VG is created and determines the minimum allocation granularity. With the default 4 MB PE size:

  • A 1 GB LV uses 256 PEs
  • A 10 GB LV uses 2560 PEs
  • Each PE maps to exactly one 4 MB chunk on a physical disk

Larger PE sizes reduce the number of PEs (simpler metadata) but waste space for small files. 4 MB is optimal for most workloads. Increase PE size only for very large VGs (>100 PVs).

# Create VG with custom PE size (16 MB):
# vgcreate -s 16M vg_data /dev/sdb /dev/sdc

# View PE information:
# pvdisplay /dev/sdb                # shows PE size, total PEs, free PEs
# vgdisplay vg_data                 # shows VG attributes and free PE count

Complete LVM Setup Walkthrough

# ── Step 1: Prepare physical disks ──────────────────────────
# Create partitions with type 8e (Linux LVM):
# fdisk /dev/sdb
Command: n → p → [Enter] → [Enter]  # entire disk
Command: t → 8e                      # set LVM type
Command: w
# partprobe /dev/sdb

# ── Step 2: Create Physical Volumes ─────────────────────────
# pvcreate /dev/sdb1 /dev/sdc1

# Verify:
# pvs                                # brief summary
# pvdisplay                          # detailed per-PV info
# pvscan                             # scan for all PVs

# ── Step 3: Create Volume Group ──────────────────────────────
# vgcreate vg_data /dev/sdb1 /dev/sdc1

# Verify:
# vgs                                # brief summary
# vgdisplay vg_data                  # detailed info
# vgscan                             # scan for all VGs

# ── Step 4: Create Logical Volume ────────────────────────────
# By size:
# lvcreate -L 30G -n lv_oracle vg_data

# By number of extents:
# lvcreate -l 100 -n lv_oracle vg_data   # 100 × PE size

# Use percentage of VG:
# lvcreate -l 50%VG -n lv_oracle vg_data
# lvcreate -l 100%FREE -n lv_data vg_data # use all remaining

# Verify:
# lvs                                # brief summary
# lvdisplay /dev/vg_data/lv_oracle   # detailed info
# lvscan                             # scan for all LVs

# ── Step 5: Format and Mount ─────────────────────────────────
# mkfs.xfs /dev/vg_data/lv_oracle
# mkdir /oracle
# mount /dev/vg_data/lv_oracle /oracle
# df -hT /oracle

# Permanent (add to /etc/fstab):
/dev/vg_data/lv_oracle  /oracle  xfs  defaults  0  0

Extending Logical Volumes — Online, No Downtime

This is LVM's greatest advantage — you can grow a filesystem while it is live and being used. The process is:

  1. Ensure the VG has free extents (add a new PV if needed)
  2. Extend the LV with lvextend
  3. Resize the filesystem to use the new space
# Check available free space:
# vgdisplay vg_data | grep "Free PE"
# vgs vg_data                       # VFree column shows free space

# Option A: Extend LV and filesystem in one command (ext4):
# lvresize -L +10G --resizefs /dev/vg_data/lv_oracle

# Option B: Manual two-step
# Extend LV:
# lvextend -L +10G /dev/vg_data/lv_oracle
# lvextend -l +100%FREE /dev/vg_data/lv_oracle  # use all free space

# Resize filesystem:
# resize2fs /dev/vg_data/lv_oracle   # ext4 — online
# xfs_growfs /oracle                 # xfs — must specify mount point

# Verify:
# df -hT /oracle
# lvdisplay /dev/vg_data/lv_oracle

Extending the Volume Group — Adding New Disks

# When the VG runs low on free extents, add a new disk:

# 1. Physically add/attach new disk
# 2. Partition it:
# fdisk /dev/sdd → n → t(8e) → w
# partprobe /dev/sdd

# 3. Create PV:
# pvcreate /dev/sdd1

# 4. Add to VG:
# vgextend vg_data /dev/sdd1

# 5. Verify increased free space:
# vgdisplay vg_data

Reducing Logical Volumes — Requires Downtime

Important: Reducing an LV is destructive if done incorrectly. XFS filesystems cannot be shrunk — only ext2/3/4 support reduction. Always back up data before reducing.

# Procedure for ext4 LV reduction:
# 1. Unmount:
# umount /oracle

# 2. Check filesystem consistency:
# e2fsck -f /dev/vg_data/lv_oracle

# 3. Resize filesystem FIRST (must be smaller than new LV size):
# resize2fs /dev/vg_data/lv_oracle 20G    # resize to 20G

# 4. Reduce LV:
# lvreduce -L 20G /dev/vg_data/lv_oracle

# 5. Remount:
# mount /dev/vg_data/lv_oracle /oracle
# df -hT /oracle

# WARNING: If you reduce LV before filesystem, data will be truncated

Data Migration with pvmove

pvmove is used when a physical disk is failing or being replaced. It moves all LV data from one PV to another while the filesystem remains mounted and in use.

# Scenario: /dev/sdb1 is failing, /dev/sdd1 is new

# Step 1: Check which LVs have data on the failing PV:
# pvdisplay -m /dev/sdb1            # shows LE to PE mapping
# lvdisplay -m /dev/vg_data/lv_oracle  # shows which PVs the LV uses

# Step 2: Add replacement PV:
# pvcreate /dev/sdd1
# vgextend vg_data /dev/sdd1

# Step 3: Migrate data (can take time — proportional to data size):
# pvmove /dev/sdb1 /dev/sdd1        # migrate specific PV to specific PV
# pvmove /dev/sdb1                  # migrate to any available PVs in VG

# pvmove runs in background; use vgdisplay to monitor progress

# Step 4: Remove failing PV:
# vgreduce vg_data /dev/sdb1
# pvremove /dev/sdb1

# Step 5: Verify:
# pvs                                # /dev/sdb1 should be gone
# lvdisplay -m /dev/vg_data/lv_oracle  # should show /dev/sdd1

LVM Snapshots

A snapshot is a point-in-time copy of an LV. It uses copy-on-write (COW) — only blocks that are changed after the snapshot is created are duplicated. Snapshots are excellent for consistent backups of live databases.

# Create snapshot (must specify size for COW area):
# lvcreate -L 5G -s -n snap_oracle /dev/vg_data/lv_oracle

# Mount snapshot read-only:
# mkdir /snap_oracle
# mount -o ro /dev/vg_data/snap_oracle /snap_oracle

# Backup from snapshot (consistent view of data):
# tar -czvf /backup/oracle_$(date +%Y%m%d).tar.gz /snap_oracle/

# Restore from snapshot (revert LV to snapshot state):
# umount /oracle
# lvconvert --merge /dev/vg_data/snap_oracle
# mount /oracle

# Remove snapshot when done:
# lvremove /dev/vg_data/snap_oracle

Recovering a Deleted or Corrupted Volume Group

# LVM automatically backs up VG metadata to /etc/lvm/backup/ after every change
# Archives are in /etc/lvm/archive/

# List available backups:
# vgcfgrestore --list vg_data
# ls /etc/lvm/archive/vg_data_*

# Restore VG from backup:
# lvremove /dev/vg_data/lv_oracle   # remove current (possibly broken) LV
# vgcfgrestore -f /etc/lvm/archive/vg_data_00001-xxxxxxx.vg vg_data

# Activate:
# vgscan
# lvscan
# vgchange -ay vg_data              # activate all LVs in VG
# lvchange -ay /dev/vg_data/lv_oracle
# mount -a

LVM Attribute Meaning

# vgs output - VG Attr column:
# w = writable
# z = resizable (extendable)
# n = normal allocation policy

# lvs output - LV Attr column (8 characters):
# Position 1: Volume type (o=origin, s=snapshot, m=mirror, -)
# Position 2: Permissions (w=writable, r=read-only)
# Position 3: Allocation policy (n=normal, i=inherit, c=contiguous)
# Position 4: Fixed minor (m=yes, -)
# Position 5: State (a=active, -)
# Position 6: Device open (o=open, -)
# Position 7: Target type (-, m=mirror, s=snapshot, t=thin, r=raid)
# Position 8: Zero (z=zeroing, -)

# Example: -wi-ao---- means: normal LV, writable, normal allocation, active, open

LVM Thin Provisioning

Thin provisioning allows you to create LVs larger than the actual physical storage — useful when not all space will be used immediately. The VG only allocates actual blocks as they are written.

# Create thin pool:
# lvcreate -L 100G -T vg_data/thinpool

# Create thin volume (can be larger than pool):
# lvcreate -V 200G -T vg_data/thinpool -n thin_lv

# Monitor pool usage:
# lvs -a vg_data                    # shows Data% and Meta% columns

Key LVM Monitoring Commands — Reference

# Physical Volumes:
# pvs                                # one-line summary per PV
# pvdisplay                          # full detail all PVs
# pvdisplay /dev/sdb1                # specific PV
# pvscan                             # find all PVs
# pvdisplay -m /dev/sdb1             # show PE-to-LE mapping

# Volume Groups:
# vgs                                # one-line summary per VG
# vgdisplay vg_data                  # full detail
# vgscan                             # find all VGs
# vgs -v                             # show UUIDs

# Logical Volumes:
# lvs                                # one-line summary
# lvdisplay /dev/vg_data/lv_oracle   # full detail
# lvscan                             # find all LVs
# lvdisplay -m                       # show physical extent mapping

# Find which PV holds a specific LV:
# lvdisplay -m /dev/vg_data/lv_oracle | grep "Physical volume"