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#
- Overview
- Architecture
- Creating Volumes
- Extending Volumes
- Reducing Volumes
- Thin Provisioning
- Snapshots
- LVM on LUKS
- Caching
- RAID with LVM
- Migration Procedures
- PE Size Considerations
- Troubleshooting
- See Also
- 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 | |
| +-------------+ +-------------+ +-------------+ |
+-----------------------------------------------------------+| Layer | Component | Description |
|---|---|---|
| Bottom | Physical Volume (PV) | A disk, partition, or other block device initialized for LVM |
| Middle | Volume Group (VG) | A pool of storage created from one or more PVs |
| Top | Logical 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/sda1Volume Groups#
# Create a VG from PVs
vgcreate vg_main /dev/sda1 /dev/sdb1
# View VG information
vgs
vgdisplay vg_mainLogical 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_dataFormat 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 24. Extending Volumes#
Extending Physical Volumes#
If the underlying partition has been resized (e.g., after expanding a virtual disk):
pvresize /dev/sda1Extending Volume Groups#
# Add a new PV to an existing VG
pvcreate /dev/sdc1
vgextend vg_main /dev/sdc1Extending 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_dataIf --resizefs is not used, resize the filesystem separately:
# ext4
resize2fs /dev/vg_main/lv_data
# XFS (can only grow, not shrink)
xfs_growfs /mnt/data5. 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/dataOr use the safer combined approach:
lvreduce -L 20G --resizefs /dev/vg_main/lv_dataRemoving 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/sdc16. 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 poolMonitoring Thin Pool Usage#
# Show thin pool data and metadata usage percentage
lvs -o +lv_size,data_percent,metadata_percent vg_main/tp_mainExtending 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_mainThin Pool Autoextend#
Configure automatic extension in /etc/lvm/lvm.conf:
thin_pool_autoextend_threshold = 80
thin_pool_autoextend_percent = 20This 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_dataRestoring 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/dataThin 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_vm1Removing Snapshots#
lvremove /dev/vg_main/snap_data8. 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_swapBoot Configuration#
Ensure the initramfs includes both LUKS and LVM support. Add to /etc/crypttab:
crypt_main UUID=<luks-uuid> none luksAnd 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/secure9. 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_dataCache Modes#
| Mode | Description | Data Safety |
|---|---|---|
writethrough | Writes go to both cache and backing device | Safe (default) |
writeback | Writes go to cache first, flushed later | Faster, risk of data loss on power failure |
passthrough | Cache only on read, writes bypass cache | Useful 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/nvme0n1Removing Cache#
lvconvert --uncache vg_main/lv_dataMonitoring Cache Performance#
lvs -o +cache_read_hits,cache_read_misses,cache_write_hits,cache_write_misses vg_main/lv_data10. 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_mirrorRepairing 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_mirror11. 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 --pollpvmove 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/sdoldExporting 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_main12. PE Size Considerations#
The Physical Extent (PE) size is set during VG creation and determines the allocation granularity.
| PE Size | Max LV Size | Granularity | Use Case |
|---|---|---|---|
| 4 MiB (default) | 16 TiB (with 32-bit extent count) | Fine | General purpose |
| 16 MiB | 64 TiB | Medium | Large VGs with many LVs |
| 64 MiB | 256 TiB | Coarse | Very 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#
| Issue | Cause | Solution |
|---|---|---|
Insufficient free space when creating LV | VG has no free PEs | Check with vgs; extend VG with vgextend or reduce other LVs |
| LV not found after reboot | VG not activated at boot | Add lvm2 to initramfs; ensure lvm2-monitor.service is enabled |
pvmove fails midway | Insufficient space on target PV, or target PV failure | Run pvmove --abort to cancel; verify target PV with pvs |
| Snapshot at 100% and invalidated | Snapshot volume was too small for the amount of change | Remove the snapshot; recreate with more space; use thin snapshots instead |
Cannot open volume group after disk removal | Missing PV that was part of the VG | Use vgreduce --removemissing vg_main to remove the missing PV reference |
| Thin pool metadata full | Too many snapshots or high churn | Extend metadata: lvextend --poolmetadatasize +1G vg/pool; configure autoextend |
LV stuck in unknown state | Metadata corruption or kernel issue | Run vgck vg_main; try vgcfgrestore vg_main from backup in /etc/lvm/backup/ |
| Slow I/O on cached LV | Cache too small or wrong mode | Check hit rate with lvs -o +cache_read_hits; increase cache size; switch to writeback if safe |