STORAGE ENGINEERING

Linux LVM (Logical Volume Manager)

Category: Volume ManagementTechnologies: LVM2, device-mapper, LVM RAID, thin pools, LVM cache

LVM is the standard logical volume manager for Linux. It abstracts physical storage into flexible pools from which virtual block devices (logical volumes) can be allocated, resized, snapshotted, and mirrored without regard for underlying physical disk boundaries.

LVM architecture

LVM uses a three-layer abstraction built on the kernel's device-mapper subsystem:

LayerAbbreviationRole
Physical VolumePVA disk or partition initialized with an LVM header. The smallest unit LVM recognizes.
Volume GroupVGA pool of storage assembled from one or more PVs. Allocations are made from here.
Logical VolumeLVA virtual block device carved from a VG. Filesystems are created on LVs.

The stack reads bottom-to-top:

Physical Disks / Partitions
        ↓
Physical Volumes (PV)   ← pvcreate
        ↓
Volume Group (VG)       ← vgcreate
        ↓
Logical Volumes (LV)    ← lvcreate
        ↓
Filesystems (ext4, XFS, Btrfs…)

Device paths

Three device path formats exist for each LV. Only the first is stable and recommended for /etc/fstab and scripts:

Path formatExampleUse
/dev/VGNAME/LVNAME/dev/myvg/mylvPreferred: persistent symlink created by udev
/dev/mapper/VGNAME-LVNAME/dev/mapper/myvg-mylvInternal format, may change between releases
/dev/dm-N/dev/dm-3Kernel device node, dynamically assigned. Never use in fstab.

Physical Volume operations

Initializing a PV

# Initialize a partition
pvcreate /dev/sdb1

# Initialize multiple devices at once
pvcreate /dev/sdb1 /dev/sdc1 /dev/sdd1

# vgcreate can auto-initialize PVs, so this step is optional:
vgcreate myvg /dev/sdb1 /dev/sdc1   # PVs created implicitly

PV metadata location

The LVM disk label sits at byte offset 512 from the start of the device. The metadata area follows at the page-size offset (typically 4 KiB) and holds a circular text buffer with VG metadata. The first Physical Extent (PE) begins at the 1 MiB mark by default, leaving roughly 1020 KiB for metadata.

# Check metadata area size and first PE location
pvs -o pv_name,pe_start,mda_size /dev/sdb1

Other PV commands

# Display detailed PV info
pvdisplay /dev/sdb1

# Scan for all PVs
pvscan

# Remove LVM label (PV must not be in a VG)
pvremove /dev/sdb1

# Move all extents off a PV (for removal)
pvmove /dev/sdb1
pvmove -n mylv /dev/sdb1          # move only extents belonging to mylv
pvmove /dev/sdb1 /dev/sdc1        # move to a specific destination PV
pvmove --abort                    # abort an in-progress pvmove

Volume Group operations

# Create VG with default 4 MiB PE size
vgcreate myvg /dev/sdb1 /dev/sdc1

# Create VG with custom PE size (must be a power of 2, minimum 1 KiB)
vgcreate -s 8M myvg /dev/sdb1

# Display VG info
vgdisplay myvg

# Add a PV to an existing VG
vgextend myvg /dev/sdd1

# Remove an unused PV from a VG
vgreduce myvg /dev/sdb1

# Remove all unused PVs
vgreduce -a myvg

# Remove missing PVs (after disk failure)
vgreduce --removemissing myvg

# Rename a VG
vgrename myvg newvg

# Remove a VG entirely
vgremove myvg
PE size

The Physical Extent size is set at VG creation time and is difficult to change afterward. The default 4 MiB is fine for most workloads. For multi-terabyte volumes, a larger PE (8 or 16 MiB) reduces snapshot metadata overhead. PE size has no impact on I/O performance, device-mapper is unaware of PE boundaries.

Logical Volume operations

Creating LVs

# Linear LV (default)
lvcreate -L 20G -n mylv myvg

# Use all remaining free space
lvcreate -l 100%FREE -n mylv myvg

# Striped LV across 3 PVs (stripe size 64 KiB)
lvcreate -i 3 -I 64 -L 30G -n mylv myvg

# 2-way mirror (RAID1)
lvcreate --type raid1 -m 1 -L 10G -n mymir myvg

# RAID5 (3 data stripes + 1 parity)
lvcreate --type raid5 -i 3 -L 30G -n myr5 myvg

# RAID10 (2 stripes, 1 mirror)
lvcreate --type raid10 -i 2 -m 1 -L 40G -n myr10 myvg

Display and scan

lvdisplay /dev/myvg/mylv
lvdisplay -m /dev/myvg/mylv     # show mapping (which PVs hold the data)
lvs                             # compact table
lvscan

Resizing logical volumes

Growing (online)

# Extend by 5 GiB and resize the filesystem in one step
lvextend -L +5G -r /dev/myvg/mylv

# Extend to use all free space
lvextend -l +100%FREE -r /dev/myvg/mylv

# Two-step (manual filesystem resize)
lvextend -L +5G /dev/myvg/mylv
resize2fs /dev/myvg/mylv          # ext4
xfs_growfs /mnt/data              # XFS, use mount point, not device

Shrinking (ext4 only, XFS cannot shrink)

DANGER: shrink filesystem before LV

Always resize the filesystem to a size smaller than the target LV size first, then reduce the LV. Reducing the LV while the filesystem still spans the full size destroys data.

# Safe shrink procedure (ext4)
umount /mnt/data
e2fsck -f /dev/myvg/mylv
resize2fs /dev/myvg/mylv 15G
lvreduce -L 15G /dev/myvg/mylv
mount /dev/myvg/mylv /mnt/data

# One-step with --resizefs (handles both)
lvreduce -L 15G -r /dev/myvg/mylv

Snapshots

LVM snapshots use copy-on-write (COW). When the snapshot is created it is empty and shares all data blocks with the origin. When a block in the origin is overwritten, the old data is copied into the snapshot's exception store first. The snapshot only grows as the origin changes.

Thick (COW) snapshots

# Create a 2 GiB snapshot
lvcreate -L 2G -s -n mysnap myvg/mylv

# If the snapshot fills up it becomes invalid, extend it
lvextend -L +1G /dev/myvg/mysnap

Size the snapshot to hold the expected write volume during its lifetime. 15-25% of the origin size is typical for short-lived backup snapshots.

Thin snapshots (no size needed)

# Thin snapshot of a thin LV
lvcreate -s -n mythinsnap myvg/mythinlv

# Merge a snapshot back into origin
lvconvert --merge myvg/mysnap

For the root filesystem, merge is deferred until the next reboot because the origin cannot be closed while active.

Thin provisioning

# Create a thin pool
lvcreate -L 10G -T myvg/mythinpool

# Create a thin LV with a virtual size larger than the pool
lvcreate -V 100G --thinpool mythinpool -n mythinlv myvg
Over-provisioning risk

If the pool fills up, every thin LV using it will fail with I/O errors. Monitor pool usage with lvs and grow the pool before it is exhausted. Set --errorwhenfull y to fail writes cleanly rather than corrupting data.

LVM RAID vs mdadm

LVM RAID uses the same kernel MD driver as mdadm but is managed through lvcreate and lvconvert. It creates hidden sub-LVs for data (_rimage_0, _rimage_1, …) and metadata (_rmeta_0, _rmeta_1, …). View them with lvs -a.

FeaturemdadmLVM RAID
MaturityVery mature, widely deployedGood, Red Hat default for new installs
Snapshots on RAIDNo (need LVM on top)Yes: native
Resizingmdadm --grow (reshaping)lvextend / lvreduce
ToolingStandalone mdadmIntegrated with LVM commands

LVM cache (dm-cache / dm-writecache)

# Create a cache pool on a fast NVMe device
lvcreate --type cache-pool -L 10G --cachemode writeback \
  -n fast_pool myvg /dev/nvme0n1p1

# Attach the cache pool to an existing LV
lvconvert --type cache --cachepool fast_pool myvg/main_lv

# Simpler: use a cache volume directly
lvcreate -L 10G -n fast_vol myvg /dev/nvme0n1p1
lvconvert --type cache --cachevol fast_vol myvg/main_lv

# Remove cache
lvconvert --splitcache myvg/main_lv
Cache modeBehavior
writethroughWrites go to both cache and origin. Safer: no data loss on cache failure.
writebackWrites go to cache only, flushed later. Faster but risk of data loss on cache device failure.
passthroughCache disabled, I/O passes through to origin.

LVM on LUKS vs LUKS on LVM

LayoutProsCons
LVM on LUKS (recommended)
Disk → LUKS → LVM → FS
Single passphrase, VG metadata encrypted, Red Hat/Fedora/Ubuntu defaultCannot encrypt individual LVs with different keys
LUKS on LVM
Disk → LVM → LUKS per LV → FS
Per-LV encryption keys, mix encrypted and unencryptedMultiple passphrases, LVM metadata exposed, slower boot

Moving VGs between hosts

# On source host:
vgchange -an myvg          # deactivate
vgexport myvg              # export (clears system ID)

# Physically move disks to new host

# On destination host:
vgimport myvg              # import (sets system ID to this host)
vgchange -ay myvg          # activate

Metadata backup and recovery

LVM automatically backs up VG metadata on every configuration change. Current metadata lives in /etc/lvm/backup/ and historical archives in /etc/lvm/archive/.

# Manual backup
vgcfgbackup myvg
vgcfgbackup -f /root/myvg.conf myvg

# List available archives
vgcfgrestore --list myvg

# Dry-run restore
vgcfgrestore --test --file /etc/lvm/archive/myvg_00003-xxx.vg myvg

# Actual restore
vgcfgrestore --file /etc/lvm/archive/myvg_00003-xxx.vg myvg
Metadata backup ≠ data backup

vgcfgbackup only saves LVM configuration (PV/VG/LV layout, sizes, mappings). It does not back up filesystem data.

Autoactivation and systemd

Modern LVM uses event-based autoactivation driven by udev. When a device appears, pvscan --cache -aay runs via a systemd service, records the PV as online, and activates the VG if all its PVs are present.

# Disable autoactivation for a VG
vgchange --setautoactivation n myvg

# Control via lvm.conf
# auto_activation_volume_list = [ "vg1", "vg2/lv1" ]  # only these
# auto_activation_volume_list = []                      # none

The lvmetad daemon is deprecated and removed in newer LVM versions. Native async disk scanning has replaced it.

Full stack walkthrough

# 1. Partition the disk (type 8E for MBR, or 8e00 for GPT)
fdisk /dev/sdb

# 2. Initialize PV (or let vgcreate do it)
pvcreate /dev/sdb1

# 3. Create VG
vgcreate myvg /dev/sdb1

# 4. Create LV
lvcreate -L 20G -n mylv myvg

# 5. Create filesystem
mkfs.ext4 /dev/myvg/mylv

# 6. Mount and add to fstab
mkdir /mnt/data
mount /dev/myvg/mylv /mnt/data
echo "/dev/myvg/mylv /mnt/data ext4 defaults 0 2" >> /etc/fstab

Command quick reference

OperationCommand
Create PVpvcreate /dev/sdb1
Create VGvgcreate myvg /dev/sdb1
Create LVlvcreate -L 10G -n mylv myvg
Extend LV + FSlvextend -L +5G -r /dev/myvg/mylv
Snapshotlvcreate -L 2G -s -n snap myvg/mylv
Merge snapshotlvconvert --merge myvg/snap
Thin poollvcreate -L 10G -T myvg/pool
Thin LVlvcreate -V 100G --thinpool pool -n thin myvg
Metadata backupvgcfgbackup myvg
Metadata restorevgcfgrestore --file <archive> myvg
Export VGvgexport myvg
Import VGvgimport myvg