LVM (Logical Volume Manager) is a device-mapper framework for Linux that abstracts physical storage into flexible logical volumes, supporting dynamic resizing, snapshots, thin provisioning, caching, and RAID.

Table of Contents#

  1. Overview
  2. Architecture
  3. Creating Volumes
  4. Extending Volumes
  5. Reducing Volumes
  6. Thin Provisioning
  7. Snapshots
  8. LVM on LUKS
  9. Caching
  10. RAID with LVM
  11. Migration Procedures
  12. PE Size Considerations
  13. Troubleshooting
  14. See Also
  15. Sources

1. Overview#

LVM provides a layer of abstraction between physical storage devices and the filesystems mounted on them. Instead of working directly with disk partitions, LVM groups physical volumes into a pool (volume group) that can be subdivided into logical volumes.

Key advantages over traditional partitioning:

  • Dynamic resizing - grow or shrink volumes without repartitioning
  • Snapshots - point-in-time copies for backup or testing
  • Thin provisioning - allocate more virtual space than physical capacity
  • Caching - use fast SSDs to cache slow HDDs
  • Striping and mirroring - software RAID at the volume level
  • Live migration - move data between physical volumes while online

2. Architecture#

+-----------------------------------------------------------+
|                    Logical Volumes (LVs)                   |
|  +-------------+  +-------------+  +-------------+       |
|  |   lv_root   |  |   lv_home   |  |   lv_data   |       |
|  |    30 GiB   |  |    50 GiB   |  |   100 GiB   |       |
|  +------+------+  +------+------+  +------+------+       |
+---------+------------------+------------------+-----------+
          |                  |                  |
+-----------------------------------------------------------+
|                     Volume Group (VG)                      |
|                       vg_main                              |
|                      180 GiB total                         |
+-----------------------------------------------------------+
          |                  |                  |
+-----------------------------------------------------------+
|                   Physical Volumes (PVs)                   |
|  +-------------+  +-------------+  +-------------+       |
|  |  /dev/sda1  |  |  /dev/sdb1  |  |  /dev/sdc   |       |
|  |    60 GiB   |  |    60 GiB   |  |    60 GiB   |       |
|  +-------------+  +-------------+  +-------------+       |
+-----------------------------------------------------------+
LayerComponentDescription
BottomPhysical Volume (PV)A disk, partition, or other block device initialized for LVM
MiddleVolume Group (VG)A pool of storage created from one or more PVs
TopLogical Volume (LV)A virtual block device allocated from a VG; formatted with a filesystem

Data is stored in fixed-size Physical Extents (PEs), which are the smallest unit of allocation (default 4 MiB).

3. Creating Volumes#

Physical Volumes#

# Initialize disks or partitions as PVs
pvcreate /dev/sda1 /dev/sdb1

# View PV information
pvs
pvdisplay /dev/sda1

Volume Groups#

# Create a VG from PVs
vgcreate vg_main /dev/sda1 /dev/sdb1

# View VG information
vgs
vgdisplay vg_main

Logical Volumes#

# Allocate all free space
lvcreate -l 100%FREE -n lv_data vg_main

# Allocate a specific size
lvcreate -L 50G -n lv_home vg_main

# Allocate a percentage of VG size
lvcreate -l 50%VG -n lv_root vg_main

# View LV information
lvs
lvdisplay /dev/vg_main/lv_data

Format and Mount#

mkfs.ext4 /dev/vg_main/lv_data
mkdir -p /mnt/data
mount /dev/vg_main/lv_data /mnt/data

# fstab entry (use LVM path for clarity)
/dev/vg_main/lv_data  /mnt/data  ext4  defaults  0 2

4. Extending Volumes#

Extending Physical Volumes#

If the underlying partition has been resized (e.g., after expanding a virtual disk):

pvresize /dev/sda1

Extending Volume Groups#

# Add a new PV to an existing VG
pvcreate /dev/sdc1
vgextend vg_main /dev/sdc1

Extending Logical Volumes#

# Extend by a specific amount
lvextend -L +20G /dev/vg_main/lv_data

# Extend to use all free space in the VG
lvextend -l +100%FREE /dev/vg_main/lv_data

# Extend LV and resize filesystem in one step
lvextend -l +100%FREE --resizefs /dev/vg_main/lv_data

If --resizefs is not used, resize the filesystem separately:

# ext4
resize2fs /dev/vg_main/lv_data

# XFS (can only grow, not shrink)
xfs_growfs /mnt/data

5. Reducing Volumes#

Reducing volumes requires careful ordering to avoid data loss. XFS cannot be shrunk.

Reducing a Logical Volume (ext4)#

# Unmount the filesystem
umount /mnt/data

# Check the filesystem
e2fsck -f /dev/vg_main/lv_data

# Shrink the filesystem first
resize2fs /dev/vg_main/lv_data 20G

# Then shrink the LV to match
lvreduce -L 20G /dev/vg_main/lv_data

# Remount
mount /dev/vg_main/lv_data /mnt/data

Or use the safer combined approach:

lvreduce -L 20G --resizefs /dev/vg_main/lv_data

Removing a PV from a Volume Group#

# Move all data off the PV
pvmove /dev/sdc1

# Remove the PV from the VG
vgreduce vg_main /dev/sdc1

# Optionally wipe the PV metadata
pvremove /dev/sdc1

6. Thin Provisioning#

Thin provisioning allows over-committing storage. Logical volumes draw from a shared thin pool, and physical space is allocated only as data is actually written.

Creating a Thin Pool#

# Create a thin pool (allocate 100 GiB for data, metadata auto-sized)
lvcreate -L 100G --thinpool tp_main vg_main

# Create thin volumes (can exceed pool size)
lvcreate -V 50G --thin -n lv_vm1 vg_main/tp_main
lvcreate -V 50G --thin -n lv_vm2 vg_main/tp_main
lvcreate -V 50G --thin -n lv_vm3 vg_main/tp_main
# Total virtual: 150 GiB from a 100 GiB pool

Monitoring Thin Pool Usage#

# Show thin pool data and metadata usage percentage
lvs -o +lv_size,data_percent,metadata_percent vg_main/tp_main

Extending the Thin Pool#

# Extend the thin pool data space
lvextend -L +50G vg_main/tp_main

# Extend the thin pool metadata (if approaching 100%)
lvextend --poolmetadatasize +1G vg_main/tp_main

Thin Pool Autoextend#

Configure automatic extension in /etc/lvm/lvm.conf:

thin_pool_autoextend_threshold = 80
thin_pool_autoextend_percent = 20

This automatically extends the thin pool by 20% when it reaches 80% usage.

7. Snapshots#

Traditional (Thick) Snapshots#

Thick snapshots use a copy-on-write mechanism with a pre-allocated snapshot volume:

# Create a snapshot (allocate space for changed blocks)
lvcreate -L 10G -s -n snap_data /dev/vg_main/lv_data

# Mount the snapshot read-only
mount -o ro /dev/vg_main/snap_data /mnt/snap

# Check snapshot usage (must not reach 100%)
lvs /dev/vg_main/snap_data

Restoring from a Snapshot#

# Unmount the original volume
umount /mnt/data

# Merge the snapshot back (reverts to snapshot state)
lvconvert --merge /dev/vg_main/snap_data

# Reactivate and remount
lvchange -an /dev/vg_main/lv_data
lvchange -ay /dev/vg_main/lv_data
mount /dev/vg_main/lv_data /mnt/data

Thin Snapshots#

Thin snapshots are more efficient; they share the thin pool and require no pre-allocated space:

# Create a thin snapshot
lvcreate -s -n snap_vm1 vg_main/lv_vm1

# Thin snapshots can themselves be snapshotted (snapshot chains)
lvcreate -s -n snap_vm1_v2 vg_main/snap_vm1

Removing Snapshots#

lvremove /dev/vg_main/snap_data

8. LVM on LUKS#

A common setup encrypts the entire volume group with LUKS, then creates LVM volumes inside:

/dev/sda1 (boot, unencrypted)
/dev/sda2 -> LUKS -> PV -> VG -> LV (root, home, swap)

Setup#

# Create the LUKS container
cryptsetup luksFormat /dev/sda2

# Open the LUKS container
cryptsetup luksOpen /dev/sda2 crypt_main

# Create LVM on top
pvcreate /dev/mapper/crypt_main
vgcreate vg_crypt /dev/mapper/crypt_main
lvcreate -L 30G -n lv_root vg_crypt
lvcreate -L 8G -n lv_swap vg_crypt
lvcreate -l 100%FREE -n lv_home vg_crypt

# Format
mkfs.ext4 /dev/vg_crypt/lv_root
mkfs.ext4 /dev/vg_crypt/lv_home
mkswap /dev/vg_crypt/lv_swap

Boot Configuration#

Ensure the initramfs includes both LUKS and LVM support. Add to /etc/crypttab:

crypt_main  UUID=<luks-uuid>  none  luks

And update /etc/fstab with the LVM device paths.

Alternative: LUKS on LVM#

Create LVs first, then encrypt each individually. This allows different encryption keys per volume but is less common:

lvcreate -L 50G -n lv_secure vg_main
cryptsetup luksFormat /dev/vg_main/lv_secure
cryptsetup luksOpen /dev/vg_main/lv_secure secure
mkfs.ext4 /dev/mapper/secure

9. Caching#

LVM caching places a fast SSD in front of a slow HDD volume, transparently caching hot data.

Creating a Cache#

# Add the SSD to the volume group
pvcreate /dev/nvme0n1
vgextend vg_main /dev/nvme0n1

# Create cache data and metadata LVs on the SSD
lvcreate -L 50G -n cache_data vg_main /dev/nvme0n1
lvcreate -L 1G -n cache_meta vg_main /dev/nvme0n1

# Create a cache pool
lvconvert --type cache-pool --cachemode writethrough \
  --poolmetadata vg_main/cache_meta vg_main/cache_data

# Attach the cache to the target LV
lvconvert --type cache --cachepool vg_main/cache_data vg_main/lv_data

Cache Modes#

ModeDescriptionData Safety
writethroughWrites go to both cache and backing deviceSafe (default)
writebackWrites go to cache first, flushed laterFaster, risk of data loss on power failure
passthroughCache only on read, writes bypass cacheUseful for warming the cache

Simplified Cache Creation (dm-cache)#

# One-step cache creation
lvcreate --type cache --cachemode writethrough \
  -L 50G -n cache vg_main/lv_data /dev/nvme0n1

Removing Cache#

lvconvert --uncache vg_main/lv_data

Monitoring Cache Performance#

lvs -o +cache_read_hits,cache_read_misses,cache_write_hits,cache_write_misses vg_main/lv_data

10. RAID with LVM#

LVM supports RAID at the logical volume level using dm-raid:

# Create a RAID 1 mirror
lvcreate --type raid1 -m 1 -L 50G -n lv_mirror vg_main

# Create a RAID 5 volume
lvcreate --type raid5 -i 3 -L 100G -n lv_raid5 vg_main

# Convert an existing linear LV to RAID 1
lvconvert --type raid1 -m 1 /dev/vg_main/lv_data

# Convert RAID 5 to RAID 6
lvconvert --type raid6 --stripes 4 /dev/vg_main/lv_raid5

# Convert back to linear
lvconvert --type linear /dev/vg_main/lv_mirror

# Check sync status
lvs -o +raid_sync_action,raid_mismatch_count vg_main/lv_mirror

Repairing RAID#

# Automatic repair
lvconvert --repair /dev/vg_main/lv_mirror

# Check and repair sync
lvchange --syncaction check /dev/vg_main/lv_mirror
lvchange --syncaction repair /dev/vg_main/lv_mirror

11. Migration Procedures#

Moving Data Between Physical Volumes#

# Move all LV extents from one PV to another
pvmove /dev/sda1 /dev/sdc1

# Move a specific LV
pvmove -n lv_data /dev/sda1 /dev/sdc1

# Monitor progress
pvmove --poll

pvmove operates online; the volume remains accessible during migration.

Migrating to a New Disk#

# Add the new disk to the VG
pvcreate /dev/sdnew
vgextend vg_main /dev/sdnew

# Move all data from the old disk
pvmove /dev/sdold /dev/sdnew

# Remove the old disk
vgreduce vg_main /dev/sdold
pvremove /dev/sdold

Exporting and Importing Volume Groups#

For moving VGs between systems:

# On the source system
vgchange -an vg_main
vgexport vg_main

# Move the physical disks to the target system

# On the target system
pvscan
vgimport vg_main
vgchange -ay vg_main

12. PE Size Considerations#

The Physical Extent (PE) size is set during VG creation and determines the allocation granularity.

PE SizeMax LV SizeGranularityUse Case
4 MiB (default)16 TiB (with 32-bit extent count)FineGeneral purpose
16 MiB64 TiBMediumLarge VGs with many LVs
64 MiB256 TiBCoarseVery large storage pools
# Create a VG with a custom PE size
vgcreate -s 16M vg_large /dev/sda1

# Check current PE size
vgdisplay vg_main | grep "PE Size"

Note: PE size cannot be changed after VG creation. For modern systems with large storage, the default 4 MiB is usually appropriate. Only increase it if you need to exceed the maximum LV size for the default PE.

13. Troubleshooting#

IssueCauseSolution
Insufficient free space when creating LVVG has no free PEsCheck with vgs; extend VG with vgextend or reduce other LVs
LV not found after rebootVG not activated at bootAdd lvm2 to initramfs; ensure lvm2-monitor.service is enabled
pvmove fails midwayInsufficient space on target PV, or target PV failureRun pvmove --abort to cancel; verify target PV with pvs
Snapshot at 100% and invalidatedSnapshot volume was too small for the amount of changeRemove the snapshot; recreate with more space; use thin snapshots instead
Cannot open volume group after disk removalMissing PV that was part of the VGUse vgreduce --removemissing vg_main to remove the missing PV reference
Thin pool metadata fullToo many snapshots or high churnExtend metadata: lvextend --poolmetadatasize +1G vg/pool; configure autoextend
LV stuck in unknown stateMetadata corruption or kernel issueRun vgck vg_main; try vgcfgrestore vg_main from backup in /etc/lvm/backup/
Slow I/O on cached LVCache too small or wrong modeCheck hit rate with lvs -o +cache_read_hits; increase cache size; switch to writeback if safe

See Also#

Sources#