LVM (Logical Volume Manager)

Quick Reference

# View current state
pvs                           # Physical volumes
vgs                           # Volume groups
lvs                           # Logical volumes
lsblk                         # Block devices

# Create basic LVM setup
pvcreate /dev/sdb             # Initialize PV
vgcreate vg_data /dev/sdb     # Create VG
lvcreate -L 100G -n lv_data vg_data  # Create LV
mkfs.ext4 /dev/vg_data/lv_data       # Format

# Extend volume
lvextend -L +50G /dev/vg_data/lv_data
resize2fs /dev/vg_data/lv_data       # Grow filesystem

# Snapshots
lvcreate -s -L 10G -n snap_data /dev/vg_data/lv_data

Understanding LVM

LVM Architecture

┌─────────────────────────────────────────────────────────────┐
│                     Logical Volumes (LV)                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │  lv_root    │  │  lv_home    │  │      lv_data        │  │
│  │  50GB       │  │  100GB      │  │      200GB          │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     Volume Group (VG)                        │
│                        vg_system                             │
│                        Total: 500GB                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                   Physical Volumes (PV)                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │  /dev/sda2  │  │  /dev/sdb   │  │     /dev/sdc        │  │
│  │  200GB      │  │  150GB      │  │     150GB           │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Physical Disks/Partitions                 │
│          /dev/sda2          /dev/sdb          /dev/sdc       │
└─────────────────────────────────────────────────────────────┘

Key Concepts

Component Description

Physical Volume (PV)

Block device (disk/partition) initialized for LVM. Divided into Physical Extents (PE).

Volume Group (VG)

Pool of storage from one or more PVs. Allocates space to Logical Volumes.

Logical Volume (LV)

Virtual partition carved from VG. Appears as /dev/vg_name/lv_name.

Physical Extent (PE)

Smallest unit of allocation (default 4MB). VG divides PVs into PEs.

Logical Extent (LE)

Maps to PE. LV size = number of LEs × PE size.

Why Use LVM?

  • Flexible resizing - Grow/shrink volumes without unmounting (for some filesystems)

  • Snapshots - Point-in-time copies for backups

  • Spanning disks - Create volumes larger than single disk

  • Striping - Improve I/O performance

  • Mirroring - Redundancy without hardware RAID

  • Thin provisioning - Overcommit storage

  • Easy migration - Move data between physical disks

Physical Volumes (PV)

View Physical Volumes

# Simple list
pvs

# Output:
#   PV         VG        Fmt  Attr PSize   PFree
#   /dev/sda2  vg_system lvm2 a--  199.00g 49.00g
#   /dev/sdb   vg_data   lvm2 a--  500.00g 200.00g

# Detailed view
pvdisplay

# Specific PV
pvdisplay /dev/sda2

# Show PE allocation
pvs -o +pv_used,pv_pe_count,pv_pe_alloc_count

# Scan for PVs
pvscan

Create Physical Volume

# On whole disk (recommended)
pvcreate /dev/sdb

# On partition
pvcreate /dev/sda3

# With specific PE size (default 4MB)
pvcreate --dataalignmentoffset 0 -M2 /dev/sdb

# Multiple disks at once
pvcreate /dev/sdb /dev/sdc /dev/sdd

# Force (wipe existing signatures)
pvcreate -ff /dev/sdb

Remove Physical Volume

# First, move data off the PV
pvmove /dev/sdb

# Then remove from VG
vgreduce vg_data /dev/sdb

# Finally, wipe PV metadata
pvremove /dev/sdb

Check/Repair Physical Volume

# Check PV metadata
pvck /dev/sdb

# Repair (use with caution)
pvck --repair /dev/sdb

Volume Groups (VG)

View Volume Groups

# Simple list
vgs

# Output:
#   VG        #PV #LV #SN Attr   VSize   VFree
#   vg_data     2   3   0 wz--n- 699.99g 199.99g
#   vg_system   1   2   0 wz--n- 199.00g  49.00g

# Detailed view
vgdisplay

# Specific VG
vgdisplay vg_data

# Show all attributes
vgs -o +vg_extent_size,vg_extent_count,vg_free_count

# Scan for VGs
vgscan

Create Volume Group

# Basic creation
vgcreate vg_data /dev/sdb

# With multiple PVs
vgcreate vg_data /dev/sdb /dev/sdc

# With specific PE size (must be power of 2, min 1KB)
vgcreate -s 16M vg_data /dev/sdb

# With metadata copies
vgcreate --metadatacopies 2 vg_data /dev/sdb

Extend Volume Group

# Initialize new PV
pvcreate /dev/sdc

# Add to existing VG
vgextend vg_data /dev/sdc

# Verify
vgs vg_data
pvs

Reduce Volume Group

# 1. Move data off the PV being removed
pvmove /dev/sdc

# 2. Remove PV from VG
vgreduce vg_data /dev/sdc

# 3. Optionally remove PV metadata
pvremove /dev/sdc

Rename Volume Group

# Rename VG
vgrename vg_old vg_new

# Update /etc/fstab and regenerate initramfs if root VG
# Update grub config if necessary

Remove Volume Group

# First remove all LVs
lvremove /dev/vg_data/lv_vol1
lvremove /dev/vg_data/lv_vol2

# Then remove VG
vgremove vg_data

# PVs can then be reused or removed
pvremove /dev/sdb

Split/Merge Volume Groups

# Split PV into new VG (moves LVs on that PV)
vgsplit vg_old vg_new /dev/sdc

# Merge VGs
vgmerge vg_target vg_source

Logical Volumes (LV)

View Logical Volumes

# Simple list
lvs

# Output:
#   LV      VG        Attr       LSize   Pool Origin Data%
#   lv_home vg_system -wi-ao---- 100.00g
#   lv_root vg_system -wi-ao----  50.00g
#   lv_data vg_data   -wi-a-----  200.00g

# Detailed view
lvdisplay

# Specific LV
lvdisplay /dev/vg_data/lv_data

# Show more attributes
lvs -o +lv_layout,lv_role,stripes,stripe_size

# Show device mapper path
ls -la /dev/mapper/
ls -la /dev/vg_data/

Create Logical Volume

# By size
lvcreate -L 100G -n lv_data vg_data

# By percentage of VG
lvcreate -l 50%VG -n lv_data vg_data

# By percentage of free space
lvcreate -l 100%FREE -n lv_data vg_data

# By number of extents
lvcreate -l 1000 -n lv_data vg_data

# On specific PV
lvcreate -L 100G -n lv_data vg_data /dev/sdb

# Striped (performance)
lvcreate -L 100G -n lv_data -i 2 -I 64K vg_data

# Mirrored (redundancy)
lvcreate -L 100G -n lv_data -m 1 vg_data

Extend Logical Volume

# Add specific size
lvextend -L +50G /dev/vg_data/lv_data

# Extend to specific size
lvextend -L 200G /dev/vg_data/lv_data

# Extend by percentage
lvextend -l +50%FREE /dev/vg_data/lv_data

# Use all remaining space
lvextend -l +100%FREE /dev/vg_data/lv_data

# Extend on specific PV
lvextend -L +50G /dev/vg_data/lv_data /dev/sdc

# Extend LV and resize filesystem together
lvextend -r -L +50G /dev/vg_data/lv_data

Resize Filesystem After Extending

# ext4 (online resize)
resize2fs /dev/vg_data/lv_data

# XFS (online resize, grow only)
xfs_growfs /mnt/data

# btrfs
btrfs filesystem resize max /mnt/data

Reduce Logical Volume

Always backup before reducing. Some filesystems (XFS) cannot shrink.
# 1. Unmount
umount /mnt/data

# 2. Check filesystem
e2fsck -f /dev/vg_data/lv_data

# 3. Shrink filesystem first
resize2fs /dev/vg_data/lv_data 80G

# 4. Then shrink LV
lvreduce -L 80G /dev/vg_data/lv_data

# OR do both together (safer)
lvreduce -r -L 80G /dev/vg_data/lv_data

# 5. Remount
mount /dev/vg_data/lv_data /mnt/data

Remove Logical Volume

# Unmount first
umount /mnt/data

# Remove LV
lvremove /dev/vg_data/lv_data

# Force removal (use with caution)
lvremove -f /dev/vg_data/lv_data

Rename Logical Volume

# Rename
lvrename vg_data lv_old lv_new

# Or with full path
lvrename /dev/vg_data/lv_old /dev/vg_data/lv_new

# Update /etc/fstab

Snapshots

Understanding Snapshots

  • Copy-on-write - Only stores changes from original

  • Space efficient - Grows as original changes

  • Consistent backups - Freeze point-in-time state

  • Rollback capability - Revert to snapshot state

Create Snapshot

# Basic snapshot
lvcreate -s -L 10G -n snap_data /dev/vg_data/lv_data

# Parameters:
#   -s          : snapshot type
#   -L 10G      : snapshot size (for COW changes)
#   -n snap_... : snapshot name

# Snapshot should be ~15-20% of origin size (adjust based on change rate)

# View snapshots
lvs -o +snap_percent

Use Snapshot for Backup

# 1. Create snapshot
lvcreate -s -L 5G -n snap_backup /dev/vg_data/lv_data

# 2. Mount snapshot (read-only recommended)
mkdir -p /mnt/snap_backup
mount -o ro /dev/vg_data/snap_backup /mnt/snap_backup

# 3. Perform backup
rsync -av /mnt/snap_backup/ /backup/destination/
# or
tar czf /backup/data-$(date +%F).tar.gz /mnt/snap_backup/

# 4. Unmount and remove snapshot
umount /mnt/snap_backup
lvremove /dev/vg_data/snap_backup

Revert to Snapshot

# WARNING: This destroys all changes since snapshot

# 1. Unmount original
umount /mnt/data

# 2. Merge snapshot back (reverts original)
lvconvert --merge /dev/vg_data/snap_data

# 3. Reactivate LV (may require reboot for root)
lvchange -an /dev/vg_data/lv_data
lvchange -ay /dev/vg_data/lv_data

# 4. Mount
mount /dev/vg_data/lv_data /mnt/data

Monitor Snapshot Usage

# Check snapshot usage
lvs -o +snap_percent

# Output:
#   LV        VG      Attr       LSize   Snap%
#   snap_data vg_data swi-a-s--- 10.00g  25.50

# Alert if snapshot fills (would become invalid)
# Monitor with: lvs -o snap_percent --noheadings /dev/vg/snap

Extend Snapshot

# Extend if running low on space
lvextend -L +5G /dev/vg_data/snap_data

Thin Provisioning

Understanding Thin Provisioning

  • Overcommit storage - Allocate more than physically available

  • Space efficient - Only uses space for actual data

  • Thin pool - Shared storage pool for thin volumes

  • Automatic growth - Volumes grow as needed

Create Thin Pool

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

# Or with metadata size
lvcreate -T -L 100G --poolmetadatasize 1G vg_data/thin_pool

# View thin pool
lvs -a -o +pool_lv,data_percent,metadata_percent

Create Thin Volume

# Create thin volume (virtual size can exceed pool)
lvcreate -V 500G -T vg_data/thin_pool -n thin_vol1

# Create another from same pool
lvcreate -V 500G -T vg_data/thin_pool -n thin_vol2

# Total virtual: 1TB, actual pool: 100GB (overcommitted)

# Format and use
mkfs.ext4 /dev/vg_data/thin_vol1
mount /dev/vg_data/thin_vol1 /mnt/thin1

Thin Snapshots

# Thin snapshots are more efficient
lvcreate -s -n thin_snap /dev/vg_data/thin_vol1

# No size needed - shares pool with origin
# Thin snapshots can be used as writable volumes

Monitor Thin Pool

# Check usage
lvs -o +data_percent,metadata_percent vg_data/thin_pool

# Set threshold alerts (dmeventd)
lvchange --monitor y vg_data/thin_pool

# Extend pool when needed
lvextend -L +50G vg_data/thin_pool

Advanced Features

Striping (Performance)

# Create striped LV across multiple PVs
lvcreate -L 100G -n lv_striped -i 3 -I 256K vg_data

# Parameters:
#   -i 3      : 3 stripes (requires 3 PVs in VG)
#   -I 256K   : stripe size (should match workload)

# View stripe configuration
lvs -o +stripes,stripe_size /dev/vg_data/lv_striped

Mirroring (Redundancy)

# Create mirrored LV
lvcreate -L 100G -n lv_mirror -m 1 vg_data

# Parameters:
#   -m 1      : 1 mirror (2 copies total)

# RAID1 style mirror
lvcreate --type raid1 -L 100G -n lv_raid1 -m 1 vg_data

# View mirror status
lvs -o +devices,seg_pe_ranges /dev/vg_data/lv_mirror

RAID with LVM

# RAID0 (striping)
lvcreate --type raid0 -L 100G -n lv_raid0 -i 3 vg_data

# RAID1 (mirroring)
lvcreate --type raid1 -L 100G -n lv_raid1 -m 1 vg_data

# RAID5 (stripe with parity)
lvcreate --type raid5 -L 100G -n lv_raid5 -i 3 vg_data

# RAID6 (stripe with double parity)
lvcreate --type raid6 -L 100G -n lv_raid6 -i 4 vg_data

# RAID10 (stripe of mirrors)
lvcreate --type raid10 -L 100G -n lv_raid10 -i 2 -m 1 vg_data

Cache Volumes

# Use SSD as cache for HDD

# 1. Create cache data LV on SSD
lvcreate -L 50G -n cache_data vg_data /dev/ssd

# 2. Create cache metadata LV on SSD
lvcreate -L 1G -n cache_meta vg_data /dev/ssd

# 3. Create cache pool
lvconvert --type cache-pool --cachemode writeback \
    --poolmetadata vg_data/cache_meta vg_data/cache_data

# 4. Attach cache to origin LV
lvconvert --type cache --cachepool vg_data/cache_data vg_data/lv_slow

# View cache stats
lvs -o +cache_mode,cache_read_hits,cache_write_hits

Moving Data Between PVs

# Move all data from one PV
pvmove /dev/sdb

# Move specific LV
pvmove -n lv_data /dev/sdb /dev/sdc

# Move to specific destination
pvmove /dev/sdb /dev/sdc

# Monitor progress
pvmove -v /dev/sdb

LVM on LUKS

Create Encrypted LVM

# 1. Create LUKS container
cryptsetup luksFormat /dev/sdb

# 2. Open LUKS
cryptsetup open /dev/sdb crypt_data

# 3. Create PV on decrypted device
pvcreate /dev/mapper/crypt_data

# 4. Create VG and LV
vgcreate vg_crypt /dev/mapper/crypt_data
lvcreate -l 100%FREE -n lv_secure vg_crypt

# 5. Format and mount
mkfs.ext4 /dev/vg_crypt/lv_secure
mount /dev/vg_crypt/lv_secure /mnt/secure

Auto-Mount with crypttab

/etc/crypttab
crypt_data    /dev/sdb    none    luks
/etc/fstab
/dev/vg_crypt/lv_secure    /mnt/secure    ext4    defaults    0 2

Troubleshooting

Common Issues

LV Won’t Activate

# Check VG status
vgdisplay vg_data

# Try activating
vgchange -ay vg_data

# If partial, import
vgimportclone /dev/sdb

# Force activation (use with caution)
lvchange -ay --activationmode partial /dev/vg_data/lv_data

Missing PV

# Check what's missing
vgdisplay --partial vg_data

# If PV is truly lost, remove references
vgreduce --removemissing vg_data

# Force if needed
vgreduce --removemissing --force vg_data

Metadata Corruption

# LVM keeps metadata backups
ls /etc/lvm/archive/
ls /etc/lvm/backup/

# Restore VG from backup
vgcfgrestore -f /etc/lvm/backup/vg_data vg_data

# Restore from archive
vgcfgrestore -f /etc/lvm/archive/vg_data_00042-*.vg vg_data

Snapshot Full

# Check snapshot status
lvs -o +snap_percent

# If 100%, snapshot is invalid
# Must remove and recreate

lvremove /dev/vg_data/snap_data

Recovery Commands

# Scan for all LVM devices
pvscan
vgscan
lvscan

# Activate all VGs
vgchange -ay

# Repair metadata areas
vgck vg_data
pvck /dev/sdb

# Rebuild metadata
pvcreate --restorefile /etc/lvm/backup/vg_data --uuid "xxxxx" /dev/sdb
vgcfgrestore vg_data

Debug Information

# Verbose output
pvdisplay -vvv /dev/sdb

# Dump metadata
vgcfgbackup vg_data
cat /etc/lvm/backup/vg_data

# Check dmsetup
dmsetup ls
dmsetup info
dmsetup status

Configuration

LVM Configuration File

/etc/lvm/lvm.conf
# Key settings
devices {
    # Filter which devices LVM scans
    filter = [ "a|/dev/sd.*|", "a|/dev/nvme.*|", "r|.*|" ]

    # Metadata backup location
    backup_dir = "/etc/lvm/backup"
    archive_dir = "/etc/lvm/archive"
}

activation {
    # Auto-activate VGs at boot
    auto_activation_volume_list = [ "vg_system", "vg_data" ]
}

global {
    # Use lvmetad (metadata caching daemon)
    use_lvmetad = 1
}

Filter Devices

# Only scan specific devices
# a = accept, r = reject

filter = [ "a|/dev/sda|", "a|/dev/sdb|", "r|.*|" ]

# Exclude multipath paths
filter = [ "a|/dev/sda|", "r|/dev/sd.*|", "a|/dev/mpath.*|" ]

# Test filter
pvscan --cache -vvv

Quick Command Reference

Information Commands

# Summary views
pvs                           # Physical volumes
vgs                           # Volume groups
lvs                           # Logical volumes

# Detailed views
pvdisplay                     # PV details
vgdisplay                     # VG details
lvdisplay                     # LV details

# Scan/refresh
pvscan                        # Scan PVs
vgscan                        # Scan VGs
lvscan                        # Scan LVs

Creation Commands

# Create PV
pvcreate /dev/sdb

# Create VG
vgcreate vg_name /dev/sdb

# Create LV
lvcreate -L 100G -n lv_name vg_name

# Create snapshot
lvcreate -s -L 10G -n snap_name /dev/vg/lv

# Create thin pool
lvcreate -T -L 100G vg_name/pool_name

# Create thin volume
lvcreate -V 500G -T vg_name/pool -n thin_lv

Modification Commands

# Extend VG
vgextend vg_name /dev/sdc

# Extend LV (and resize filesystem)
lvextend -r -L +50G /dev/vg/lv

# Reduce LV (shrink filesystem first!)
lvreduce -r -L 50G /dev/vg/lv

# Move data
pvmove /dev/sdb /dev/sdc

# Activate/deactivate
vgchange -ay vg_name          # Activate VG
vgchange -an vg_name          # Deactivate VG
lvchange -ay /dev/vg/lv       # Activate LV
lvchange -an /dev/vg/lv       # Deactivate LV

Removal Commands

# Remove LV
lvremove /dev/vg/lv

# Remove VG (after removing LVs)
vgremove vg_name

# Remove PV (after removing from VG)
pvremove /dev/sdb

# Reduce VG (remove PV from VG)
vgreduce vg_name /dev/sdb

See Also