Andrew Mercer
on this page

LVM (Logical Volume Manager): The Complete Guide

LVM adds a flexible abstraction layer between physical disks and the filesystems that live on them. Instead of being locked into fixed partitions, you pool storage together and carve it into logical volumes that can be grown, shrunk, moved between disks, snapshotted, mirrored, and cached, usually without downtime.

Reference: Logical Volume Manager (Linux), Wikipedia

Conventions used in this guide

  • Volume group: vg_data. Logical volume: lv_data. Disks: /dev/sdb, /dev/sdc.
  • Commands need root (sudo).
  • Most LVM commands accept -t / --test (dry run) and -v (verbose). Use them when in doubt.
  • Destructive commands are marked with WARNING.

Table of Contents

  1. Concepts and architecture
  2. Inspecting LVM
  3. Creating LVM storage
  4. Growing storage
  5. Shrinking storage
  6. Moving data and reorganizing
  7. Renaming, removing, and deactivating
  8. Snapshots
  9. Thin provisioning
  10. RAID, mirroring, and striping
  11. Caching with dm-cache
  12. Encryption with LUKS
  13. Metadata backup, restore, and recovery
  14. Foreign and external volumes
  15. Low-level device-mapper tricks
  16. Configuration (lvm.conf)
  17. Troubleshooting
  18. Best practices
  19. Cheat sheet

1. Concepts and architecture

LVM has three main layers, built bottom to top:

   Filesystem (ext4, xfs, swap, ...)
        |
   Logical Volumes (LV)     /dev/vg_data/lv_data   (also /dev/mapper/vg_data-lv_data)
        |
   Volume Group (VG)        vg_data  (a pool of extents)
        |
   Physical Volumes (PV)    /dev/sdb1, /dev/sdc, /dev/mapper/luks-...
        |
   Block devices            disks, partitions, RAID arrays, LUKS mappings, iSCSI/SAN LUNs
Term Meaning
PV (Physical Volume) A block device initialized for LVM. It holds an LVM label and metadata.
VG (Volume Group) One or more PVs pooled together. The unit of administration.
LV (Logical Volume) A virtual block device carved from a VG. You put a filesystem (or swap, or a database) on it.
PE (Physical Extent) The allocation unit of a PV (default 4 MiB). All space is handed out in whole PEs.
LE (Logical Extent) The allocation unit of an LV. Each LE maps to one PE (or more, when mirrored).
device-mapper The kernel subsystem that implements LVM (and LUKS, multipath, and more). LVs appear as /dev/dm-N.
Metadata Text description of the VG layout stored on each PV, with backups in /etc/lvm/backup and history in /etc/lvm/archive.

Because sizes are rounded up to whole extents, an LV of "10G" may occupy slightly more than requested if the size is not a multiple of the PE size.

Common LV types: linear (default), striped, mirror/RAID (raid1, raid5, raid6, raid10), snapshot, thin pool, thin volume, cache.

Filesystem support at a glance:

Filesystem Grow online Shrink Grow tool Shrink tool
ext4 Yes Yes, offline only resize2fs resize2fs
XFS Yes Never xfs_growfs (takes the mount point) n/a
Btrfs Yes Yes (online) btrfs filesystem resize btrfs filesystem resize
JFS Yes No mount option remount,resize n/a
swap n/a n/a mkswap again mkswap again

2. Inspecting LVM

Always look before you leap.

Summaries

pvs            # one line per physical volume
vgs            # one line per volume group
lvs            # one line per logical volume
lvs -a -o +devices     # include hidden LVs and show which PVs back each LV
pvs -o +uuid           # show PV UUIDs
lsblk -f               # tree view of disks, LVs, filesystems, UUIDs, mount points

Detail

pvdisplay
vgdisplay
lvdisplay

Key fields to check before any resize:

  • pvdisplay: are the LVs you care about on the same PV / VG? (VG Name, Free PE)
  • vgdisplay: VG Size, PE Size, Alloc PE / Size, and especially Free PE / Size, which is space you can allocate right now.
  • lvs: LV sizes and the attribute string (see below).

Example vgs output:

  VG      #PV #LV #SN Attr   VSize  VFree
  vg_data   1   3   0 wz--n- 40.29g 5.84g

Reading the lvs Attr column

The attribute string is positional. Common values:

Position Meaning
1 Volume type: - linear, s snapshot, V thin volume, t thin pool, r RAID, m mirror, C cache
2 Permissions: w writable, r read-only
3 Allocation policy (i inherited)
5 State: a active, s suspended, I invalid snapshot, S invalid suspended snapshot
6 Device: o open (mounted or in use)

So -wi-ao---- means a linear, writable LV that is active and open.

Low-level device-mapper view

dmsetup ls                             # all mapped devices
dmsetup ls --tree                      # dependency tree (great for LUKS on LVM)
dmsetup info /dev/vg_data/lv_data      # state, open count, major/minor, UUID
dmsetup table                          # the actual mapping tables

Open count matters: a non-zero value means something (a mount, a process, another mapping) is holding the device open, and removal will fail.


3. Creating LVM storage

3.1 Prepare the disk

You can use a whole disk or a partition. Whole-disk PVs are simple and fine for data disks; partitions are safer on shared/boot disks because other tools can see that a partition table exists.

Partition (optional):

# GPT with one partition spanning the disk, flagged for LVM
parted -s /dev/sdb mklabel gpt mkpart primary 1MiB 100% set 1 lvm on

# or interactively
fdisk /dev/sdb      # new partition; set type to "Linux LVM" (8e00 in gdisk, 8e in fdisk/MBR)

Re-using a disk? Wipe old signatures first so old RAID/filesystem/LVM labels do not confuse things:

wipefs -a /dev/sdb1        # WARNING: destroys signatures

3.2 Create the Physical Volume

pvcreate /dev/sdb1
pvdisplay /dev/sdb1

3.3 Create the Volume Group

vgcreate vg_data /dev/sdb1
# Optional: choose a different PE size (default 4 MiB). Larger PEs suit huge VGs.
vgcreate -s 16M vg_data /dev/sdb1
vgdisplay vg_data

3.4 Create Logical Volumes

# Fixed size
lvcreate -L 10G -n lv_data vg_data

# Percentage of the VG or of the remaining free space
lvcreate -l 50%VG      -n lv_half vg_data
lvcreate -l 100%FREE   -n lv_rest vg_data      # note: %FREE, all remaining space

lvdisplay /dev/vg_data/lv_data

Size options at a glance:

Option Meaning
-L 10G Absolute size in bytes-based units
-L +5G Add 5G (extend/resize commands)
-l 2560 Size in extents
-l +100%FREE Use all remaining free space
-l 50%VG Half of the entire VG
-l 20%ORIGIN Relative to the snapshot origin

3.5 Make a filesystem

mkfs.ext4 /dev/vg_data/lv_data
# or
mkfs.xfs  /dev/vg_data/lv_data

/dev/vg_data/lv_data and /dev/mapper/vg_data-lv_data are the same device. Note that in the mapper name, any hyphen in the VG or LV name is doubled (my-vg / my-lv becomes my--vg-my--lv).

3.6 Mount it persistently

mkdir -p /data
blkid /dev/vg_data/lv_data        # copy the UUID (or use lsblk -f)

Add to /etc/fstab. Make sure the filesystem type in fstab matches what you actually created:

UUID=afa2f55b-49c2-4e16-9fba-b7fab68bfbdb  /data  xfs   defaults  0  0
# or, using the stable device path (equally valid for LVs):
/dev/mapper/vg_data-lv_data                 /data  ext4  defaults  0  2

For non-critical volumes add nofail so a missing disk does not drop the system into emergency mode.

mount -a        # validates fstab entries and mounts anything new
df -h /data

Tip: XFS uses 0 for the fsck field because it does not use fsck at boot; ext4 uses 2 for non-root filesystems.


4. Growing storage

Growing is the safe, everyday operation. It can be done online (mounted) for ext4 and XFS.

There are two questions to answer:

  1. Is there free space in the VG? If not, add capacity to the VG first (4.2 / 4.3).
  2. Extend the LV, then extend the filesystem inside it.

4.1 Extend an LV using free VG space

vgs                                  # check VFree
lvextend -L +5G /dev/vg_data/lv_data # add 5 GiB

The one-command way (recommended): -r / --resizefs grows the filesystem for you, picking the correct tool:

lvextend -r -L +5G           /dev/vg_data/lv_data
lvextend -r -l +100%FREE     /dev/vg_data/lv_data      # use all free space
lvextend -r -L 50G           /dev/vg_data/lv_data      # to an absolute size

The manual way, when you extend the LV without -r:

# ext4
resize2fs /dev/vg_data/lv_data              # grows to fill the LV
resize2fs -p /dev/vg_data/lv_data           # with progress

# XFS (argument is the MOUNT POINT, and it must be mounted)
xfs_growfs /data
xfs_growfs -D <blocks> /data                # grow to a specific size in blocks

# Generic front end that picks the right tool
fsadm resize /dev/vg_data/lv_data

Legacy note: very old systems used ext2online for online ext3 growth. It has been folded into resize2fs for many years and should no longer be needed.

4.2 Add a new disk to the VG

pvcreate /dev/sdc
vgextend vg_data /dev/sdc
lvextend -r -l +100%FREE /dev/vg_data/lv_data

To extend an LV onto a specific PV:

lvextend -r -L +20G /dev/vg_data/lv_data /dev/sdc

4.3 Grow the underlying disk (virtual disks, SAN LUNs, cloud volumes)

When the hypervisor or storage array enlarges an existing disk, tell the kernel and LVM about the new size.

Case A: PV is a whole disk

echo 1 > /sys/class/block/sdb/device/rescan     # kernel re-reads size
pvresize /dev/sdb
lvextend -r -l +100%FREE /dev/vg_data/lv_data

Case B: PV is a partition (grow the partition first)

echo 1 > /sys/class/block/sda/device/rescan
growpart /dev/sda 2          # from cloud-utils-growpart; or use parted/fdisk to resize the partition
pvresize /dev/sda2
lvextend -r -l +100%FREE /dev/vg_data/lv_data

4.4 Add a new virtual disk (VMware, KVM, cloud) without a reboot

Snapshot the disk list before and after so you can see exactly what appeared:

lsblk > /tmp/disks-pre.txt

# ...attach the disk in the hypervisor / cloud console...

# Rescan every SCSI host
for h in /sys/class/scsi_host/host*; do echo "- - -" > "$h/scan"; done
# or: rescan-scsi-bus.sh (package: sg3_utils / scsitools)

lsblk > /tmp/disks-post.txt
diff /tmp/disks-pre.txt /tmp/disks-post.txt

pvcreate /dev/sdb
vgextend vg_data /dev/sdb
lvextend -r -l +100%FREE /dev/vg_data/lv_data

Then verify with df -h and lvs.


5. Shrinking storage

Shrinking is the risky operation. The golden rule:

Shrink the filesystem first, then the LV. Grow the LV first, then the filesystem.

If you reduce the LV below the size of the filesystem, you truncate the filesystem and corrupt data. Take a backup first.

Requirements and limits

  • ext4 can shrink, but only offline (unmounted). For the root filesystem, boot from a live USB/rescue environment.
  • XFS and JFS cannot be shrunk. The only option is: back up, recreate a smaller LV and filesystem, restore.
  • Btrfs can shrink online.
  • Check the filesystem's used space first (df -h); you cannot shrink below the data in use plus overhead.

5.1 The easy way: lvreduce -r

umount /data
e2fsck -f /dev/vg_data/lv_data          # required by resize2fs before shrinking
lvreduce -r -L 5G /dev/vg_data/lv_data  # shrinks fs then LV, in the right order
mount /data

-r calls fsadm/resize2fs for you, shrinking the filesystem before the LV. LVM will prompt with a data-loss warning unless you use -f.

5.2 The manual, step-by-step way (ext4)

Example: reduce a 10 GiB volume to 5 GiB.

# 1. Unmount
umount /data

# 2. Check the filesystem (mandatory)
e2fsck -f /dev/vg_data/lv_data

# 3. Shrink the FILESYSTEM to slightly LESS than the target size
#    (resize2fs accepts whole units like 4G; fractional values such as 4.5G are rejected)
resize2fs /dev/vg_data/lv_data 4G

# 4. Shrink the LV to the target size
lvreduce -L 5G /dev/vg_data/lv_data
#   WARNING: Reducing active logical volume to 5.00 GiB
#   THIS MAY DESTROY YOUR DATA (filesystem etc.)
#   Do you really want to reduce lv_data? [y/n]: y

# 5. Grow the filesystem to fill the LV exactly (reclaims the safety margin)
resize2fs /dev/vg_data/lv_data

# 6. Remount and verify
mount /data
df -h /data

Why the safety margin? Shrinking the filesystem to 4G first, then the LV to 5G, guarantees the LV never ends up smaller than the filesystem. Step 5 then grows the filesystem into the remaining 1G.

5.3 Worked example: move free space from /home to /

A common scenario: / is nearly full while /home has plenty of room, and both are in the same VG.

  1. Are they in the same VG, and is there free space?

bash pvs; vgs; lvs

Suppose the VG has 5.8 GiB free, root is 5.6 GiB at 97% used, and home is 23 GiB at 6% used.

  1. Use the free VG space first. It needs no shrinking:

bash lvextend -r -L +5G /dev/vg/root

  1. Need more? Take it from /home (offline, from a live/rescue environment if /home cannot be unmounted, or just log out and unmount from a root shell):

bash umount /home e2fsck -f /dev/vg/home lvreduce -r -L -3G /dev/vg/home # shrink by 3 GiB mount /home lvextend -r -l +100%FREE /dev/vg/root

  1. Verify with df -h and lvs.

If a volume is on an encrypted (LUKS) layer, the layers must be shrunk in order: filesystem, then the LUKS mapping, then the LV. See section 12.

5.4 Shrinking the VG (removing a PV)

You cannot remove a PV that still holds data. Move the data off it, then remove it from the VG:

pvmove /dev/sdb                 # migrate all extents from sdb to other PVs in the VG
vgreduce vg_data /dev/sdb       # remove sdb from the VG
pvremove /dev/sdb               # wipe the LVM label (optional)
#   Removed "/dev/sdb" from volume group "vg_data"

pvmove needs enough free extents on the remaining PVs. See section 6.

5.5 Undoing a mistaken shrink

If you shrank the LV before the filesystem, stop writing to it immediately. Do not simply re-extend: LVM may not hand back the same blocks, which corrupts the filesystem. Instead, restore the earlier metadata from the archive (see section 13). This only works if the freed extents have not been reused since.


6. Moving data and reorganizing

Move extents between PVs (online)

pvmove /dev/sdb                          # everything off sdb
pvmove /dev/sdb /dev/sdc                 # everything off sdb, onto sdc specifically
pvmove -n lv_data /dev/sdb /dev/sdc      # only lv_data's extents
pvmove -i 5 /dev/sdb                     # report progress every 5 seconds

pvmove is safe to interrupt and resume: run pvmove with no arguments to continue, or pvmove --abort to cancel.

Typical uses: replacing an old disk, migrating to a faster disk, evacuating a failing disk.

Split and merge volume groups

# Merge: absorb vg_old into vg_data (vg_old must be inactive)
vgchange -an vg_old
vgmerge vg_data vg_old

# Split: move a PV (and the LVs entirely on it) into a new VG
vgsplit vg_data vg_new /dev/sdc

Migrate a whole VG to another machine

umount /data
vgchange -an vg_data
vgexport vg_data
# physically move the disks
vgimport vg_data
vgchange -ay vg_data

7. Renaming, removing, and deactivating

Rename

vgs; lvs
lvrename vg_data lv_old lv_new          # same as: lvrename vg_data/lv_old vg_data/lv_new
vgrename vg_old vg_new

After renaming, update /etc/fstab, bootloader config (root=), and initramfs if the VG or LV holds the root filesystem, or the system may not boot. Rebuild with update-initramfs -u (Debian/Ubuntu) or dracut -f (RHEL/Fedora).

Deactivate and reactivate

umount /data
lvchange -an /dev/vg_data/lv_data       # deactivate one LV
lvchange -ay /dev/vg_data/lv_data       # activate it again
vgchange -an vg_data                    # deactivate the whole VG
vgchange -ay vg_data                    # activate all LVs in the VG

(--available y is the long form of -ay.)

Remove: go in reverse order of creation

WARNING: destroys data.

umount /data                            # and remove the fstab entry
lvremove /dev/vg_data/lv_data           # 1. remove the logical volume(s)
vgremove vg_data                        # 2. remove the volume group
pvremove /dev/sdb1                      # 3. remove the physical volume label

To remove a single PV from a VG that continues to exist, use pvmove then vgreduce (section 5.4). Note that vgreduce requires the PV to be empty.


8. Snapshots

A snapshot is a point-in-time view of an LV. Classic (thick) LVM snapshots use copy-on-write: when the origin changes, the old blocks are copied into the snapshot's own space first.

8.1 Classic snapshots

# The size (-L) is the space reserved to store CHANGES, not a full copy
lvcreate -L 5G --snapshot --name lv_data_snap /dev/vg_data/lv_data
lvs

Use it for consistent backups:

mkdir -p /mnt/snap
mount -o ro /dev/vg_data/lv_data_snap /mnt/snap        # for XFS add: -o ro,nouuid
tar -C /mnt/snap -czf /backup/data.tar.gz .
umount /mnt/snap
lvremove -y /dev/vg_data/lv_data_snap

Things to know:

  • Size matters. If changes to the origin exceed the snapshot's space, the snapshot becomes invalid (Attr shows I) and is unusable. Monitor Data% in lvs and size for expected churn (10-20% of the origin is a common start).
  • Snapshots slow writes to the origin (copy-on-write overhead). Do not leave them around indefinitely.
  • You can extend a snapshot: lvextend -L +2G /dev/vg_data/lv_data_snap.
  • Automatic extension can be configured with snapshot_autoextend_threshold and snapshot_autoextend_percent in lvm.conf.
  • XFS snapshots share the origin's UUID; mount with -o nouuid if the origin is mounted at the same time.
  • Flush application state first (database checkpoint, fsfreeze) for application-consistent snapshots.

8.2 Roll back (merge) a snapshot

To revert the origin to the state at the time of the snapshot:

umount /data
lvconvert --merge /dev/vg_data/lv_data_snap
# If the origin is busy, the merge is deferred until the next activation:
lvchange -an /dev/vg_data/lv_data && lvchange -ay /dev/vg_data/lv_data
mount /data

The snapshot is consumed by the merge. Anything written to the origin after the snapshot was taken is lost.

8.3 Snapshotting a swap or system volume

Any LV can be snapshotted, e.g.:

lvcreate -L 5G --snapshot --name web-dev-snap /dev/vg01/web-dev

For VM images, snapshot the LV backing the VM, and shut down (or freeze) the guest for consistency.

Thin snapshots (section 9) avoid most of these limitations.


9. Thin provisioning

Thin provisioning lets you allocate more virtual space than physically exists ("overprovisioning") and store data only as it is written. It also gives cheap, fast, unlimited-ish snapshots.

9.1 Create a thin pool and thin volumes

# 1. Create the pool (data + metadata) from real space
lvcreate --type thin-pool -L 100G -n pool0 vg_data

# 2. Create thin volumes with a VIRTUAL size (can exceed the pool)
lvcreate --thin -V 500G -n lv_thin1 vg_data/pool0
lvcreate --thin -V 500G -n lv_thin2 vg_data/pool0

mkfs.xfs /dev/vg_data/lv_thin1

9.2 Thin snapshots

Thin snapshots need no size argument and are independent of the origin's lifetime:

lvcreate -s -n lv_thin1_snap vg_data/lv_thin1
lvchange -ay -K vg_data/lv_thin1_snap        # -K: ignore the "activation skip" flag

9.3 Monitor and protect the pool

A full thin pool is an outage. Watch it:

lvs -a -o +data_percent,metadata_percent

Enable auto-extension in lvm.conf (requires the dmeventd monitoring service):

activation {
    thin_pool_autoextend_threshold = 80
    thin_pool_autoextend_percent   = 20
}

Extend manually if needed:

lvextend -L +50G vg_data/pool0
lvextend --poolmetadatasize +1G vg_data/pool0

Return unused space to the pool by mounting with discard or running fstrim -av periodically.


10. RAID, mirroring, and striping

LVM can do RAID itself via the kernel's MD RAID code (--type raid*). Alternatively, place LVM on top of mdadm arrays. Both are valid; pick one approach and stay consistent.

10.1 Striping (performance)

# Stripe across 2 PVs with a 64 KiB stripe size
lvcreate -i 2 -I 64 -L 50G -n lv_fast vg_data

Striping has no redundancy: losing one PV loses the whole LV.

10.2 RAID1 (mirroring)

lvcreate --type raid1 -m 1 -L 50G -n lv_mirror vg_data
lvs -a -o name,copy_percent,devices              # watch the initial sync

Add or remove a mirror leg:

lvconvert -m +1 vg_data/lv_mirror        # add a third copy
lvconvert -m -1 vg_data/lv_mirror        # drop back

10.3 Other RAID levels

lvcreate --type raid5  -i 3 -L 100G -n lv_r5  vg_data     # 3 data stripes + parity (4 PVs)
lvcreate --type raid6  -i 3 -L 100G -n lv_r6  vg_data     # 3 data + 2 parity (5 PVs)
lvcreate --type raid10 -i 2 -m 1 -L 100G -n lv_r10 vg_data

10.4 Health and repair

lvs -a -o name,raid_sync_action,sync_percent,raid_mismatch_count
lvchange --syncaction check vg_data/lv_mirror       # scrub
lvchange --syncaction repair vg_data/lv_mirror
lvconvert --repair vg_data/lv_mirror                # replace a failed leg using free PVs

11. Caching with dm-cache

Use a fast device (NVMe/SSD) to accelerate a slow LV (HDD).

# 1. Add the fast device to the VG
pvcreate /dev/nvme0n1p1
vgextend vg_data /dev/nvme0n1p1

# 2. Create a cache volume on the fast device
lvcreate -n cache0 -L 50G vg_data /dev/nvme0n1p1

# 3. Attach it to the slow LV
lvconvert --type cache --cachevol cache0 vg_data/lv_data

# Optional: choose the cache mode (writethrough is safer; writeback is faster)
lvconvert --type cache --cachevol cache0 --cachemode writethrough vg_data/lv_data

Remove the cache (flushes dirty data first) with:

lvconvert --uncache vg_data/lv_data

Use writeback only with redundant cache devices, because losing the cache can lose data that was not yet flushed to the slow disk.


12. Encryption with LUKS

There are two common layouts:

Layout Description Use when
LUKS on LVM Each LV is separately encrypted: PV → VG → LV → LUKS → filesystem You want different keys per volume or to leave some volumes unencrypted.
LVM on LUKS The whole disk/partition is encrypted once: disk → LUKS → PV → VG → LV → filesystem You want one passphrase and the whole VG (incl. LV names and layout) hidden.

12.1 Add an encrypted volume to an existing system (LUKS on LVM)

# 1. (Only if re-using a disk) overwrite old data with random data
dd if=/dev/urandom of=/dev/sdb bs=1M status=progress     # WARNING: destroys all data

# 2. Partition and set up LVM as usual
pvcreate /dev/sdb1
vgcreate vg_secure /dev/sdb1
lvcreate -L 20G -n lv_secure vg_secure

# 3. Encrypt the LV
cryptsetup luksFormat /dev/vg_secure/lv_secure
cryptsetup luksOpen  /dev/vg_secure/lv_secure secure

# 4. Filesystem and mount
mkfs.ext4 /dev/mapper/secure
mount /dev/mapper/secure /mnt/secure

Persist with /etc/crypttab (secure /dev/vg_secure/lv_secure none luks) and /etc/fstab (/dev/mapper/secure /mnt/secure ext4 defaults 0 2).

12.2 Grow a LUKS-encrypted volume

Order: LV, then LUKS mapping, then filesystem.

Modern cryptsetup and ext4/XFS can do this online with the volume open and mounted:

lvextend -L +20G /dev/vg_data/lv_crypt      # 1. grow the LV
cryptsetup resize secure                    # 2. grow the LUKS mapping (name of the opened mapping)
resize2fs /dev/mapper/secure                # 3. ext4  (or: xfs_growfs /mnt/secure)
df -h /mnt/secure

If the encrypted volume is the root filesystem, the online approach above works too on current systems. On older systems, or if something goes wrong, use a live USB:

# From a live environment
cryptsetup luksOpen /dev/vg_data/lv_root encrypted
fsck.ext4 -C 0 -f /dev/mapper/encrypted         # check first
lvextend -L +20G /dev/vg_data/lv_root           # (skip if already extended online)
cryptsetup --verbose resize encrypted
fsck.ext4 -f /dev/mapper/encrypted
resize2fs /dev/mapper/encrypted
mount /dev/mapper/encrypted /mnt/encrypted
df -h /mnt/encrypted
cryptsetup luksClose encrypted                  # after unmounting

Use a mapping name that does not clash with the LV's own mapper name (for example encrypted, not vg_data-lv_root).

12.3 Shrink a LUKS-encrypted volume

Reverse order: filesystem, then LUKS, then LV. ext4 only, offline:

umount /mnt/secure
e2fsck -f /dev/mapper/secure
resize2fs /dev/mapper/secure 8G                      # 1. shrink the filesystem
cryptsetup resize secure --size $(( 9 * 1024 * 1024 * 2 ))   # 2. LUKS size in 512-byte sectors (9 GiB here, with margin)
lvreduce -L 10G /dev/vg_data/lv_secure               # 3. LV, keeping it larger than the LUKS device
cryptsetup resize secure                             # 4. grow the mapping to fill the LV
resize2fs /dev/mapper/secure                         # 5. grow the filesystem to fill it

Keep margins between layers (fs < LUKS < LV) at every step, and take a backup first.

12.4 Mount and unmount an external encrypted LVM disk

cryptsetup luksOpen /dev/sdx1 externalcrypt          # enter the passphrase
vgscan
vgchange -ay
mount /dev/mapper/vgXXX-lv_root /mnt/external

# ...when done, tear down in reverse order
umount /mnt/external
vgchange -an vgXXX
cryptsetup luksClose externalcrypt
lsblk -f

13. Metadata backup, restore, and recovery

LVM automatically saves a text copy of VG metadata before and after every change:

Location Purpose
/etc/lvm/backup/<vg> The most recent metadata for each VG
/etc/lvm/archive/<vg>_NNNNN-<random>.vg History: one file per change. Kept as long as archive = 1 in lvm.conf.

Archiving is controlled by backup and archive in /etc/lvm/lvm.conf (both default to 1). Keep them on.

Metadata is not data. Restoring metadata brings back the layout (which extents belong to which LV). It cannot recover contents overwritten after the change. Act quickly, and do not write to the affected disks.

13.1 Manual backup

vgcfgbackup                              # all VGs to /etc/lvm/backup
vgcfgbackup -f /root/vg_data.meta vg_data

Copy these files off the machine. If the disk that holds /etc/lvm dies, the on-disk metadata copy on the PV is still there, but an off-box copy is a lifesaver.

13.2 List the history

ls -ltrah /etc/lvm/archive/
vgcfgrestore -l vg_data

Each entry shows a description of the command that triggered the archive (for example, Created *before* executing 'lvreduce -L -256M ...'). Use these descriptions to pick the archive taken just before the change you want to undo.

13.3 Undo a change (restore an earlier layout)

umount /data                               # LVs of the VG must not be in use
lvchange -an /dev/vg_data/lv_data          # or vgchange -an vg_data

# Dry run first
vgcfgrestore --test -f /etc/lvm/archive/vg_data_00001-1528176681.vg vg_data

# Do it
vgcfgrestore -f /etc/lvm/archive/vg_data_00001-1528176681.vg vg_data

vgchange -ay vg_data

Then run fsck on the filesystem before mounting it.

13.4 Recover accidentally removed logical volumes

Symptom: after an lvremove, vgchange -ay reports 0 logical volume(s) in volume group "vg_data" now active.

ls -ltrah /etc/lvm/archive/                      # find archives for the VG, newest last
vgcfgrestore --test -f /etc/lvm/archive/vg_data_00002-504999937.vg vg_data
vgcfgrestore        -f /etc/lvm/archive/vg_data_00002-504999937.vg vg_data
vgchange -ay vg_data
ls /dev/mapper/

If only some LVs came back (for instance just swap), you picked a state that is too recent or too old. Try an earlier archive file (the one with the previous sequence number) until all the LVs you expect are present:

vgcfgrestore -f /etc/lvm/archive/vg_data_00001-878307462.vg vg_data
vgchange -ay vg_data

Each restore is itself archived, so you can move back and forth between states safely.

13.5 vgcfgrestore fails

"Couldn't find device with uuid ..." / "Cannot restore Volume Group ... with N PVs marked as missing".

  1. Is the PV encrypted? Open it first (cryptsetup luksOpen ...) and then retry. This is the most common cause.
  2. Check the PV UUIDs that LVM can currently see and compare them with the archive:

bash pvs -o +uuid lvs -o +devices grep -A3 'pv0 {' /etc/lvm/archive/vg_data_00002-504999937.vg # UUID the archive expects

  1. If a PV was wiped or replaced, recreate it with the original UUID using the archive as a template, then restore:

bash pvcreate --uuid <original-uuid> --restorefile /etc/lvm/archive/vg_data_00002-504999937.vg /dev/sdX vgcfgrestore -f /etc/lvm/archive/vg_data_00002-504999937.vg vg_data vgchange -ay vg_data

13.6 Permanently missing disk

If a PV is gone for good (and you have no mirror or RAID):

vgs -o +pv_missing
vgchange -ay --activationmode partial vg_data     # activate what can be activated
vgreduce --removemissing vg_data                  # drop the missing PV; LVs that used it are lost
vgreduce --removemissing --force vg_data          # also remove LVs that touched the missing PV

Copy whatever you can out of the partially active LVs first. For RAID/mirror LVs use lvconvert --repair instead (section 10.4).


14. Foreign and external volumes

14.1 Use LVM disks from another system (for example, a USB drive)

vgscan
#   Found volume group "vg_ext" using metadata type lvm2
vgchange -ay vg_ext
#   2 logical volume(s) in volume group "vg_ext" now active
mount /dev/vg_ext/lv_data /mnt/external

When you are done:

umount /mnt/external
vgchange -an vg_ext
vgexport vg_ext            # optional, marks it safe to unplug/move

14.2 Duplicate VG names

If the foreign disk has a VG with the same name as one on your machine (common with cloned root disks), LVM refuses to activate one of them. Rename by UUID:

vgs -o +vg_uuid
vgrename <vg-uuid> vg_ext_new

14.3 Using a live/rescue environment on the host's own disks

vgscan
vgchange -ay
lsblk -f
mount /dev/vg_system/lv_root /mnt

Bind-mount /dev, /proc, /sys for a chroot as needed.


15. Low-level device-mapper tricks

dmsetup talks to device-mapper directly. It is a scalpel; prefer the lv* tools where possible.

Suspend and resume I/O

Suspending freezes all I/O to the device. Processes that touch it will block until it is resumed. Useful for atomic operations such as storage-array snapshots.

dmsetup info /dev/vg_data/lv_data          # State: ACTIVE
dmsetup suspend /dev/vg_data/lv_data
dmsetup info /dev/vg_data/lv_data          # State: SUSPENDED
dmsetup resume /dev/vg_data/lv_data
dmsetup info /dev/vg_data/lv_data          # State: ACTIVE

For filesystem-level quiescing (flushes dirty data first), fsfreeze -f /data / fsfreeze -u /data is usually better.

Remove a mapping

dmsetup remove /dev/vg_data/lv_data
dmsetup remove --force <device_name>       # replaces the mapping with an error target; last resort

Prefer lvremove or lvchange -an; use dmsetup remove only for stale mappings that LVM no longer knows about.

Device or resource busy

device-mapper: remove ioctl on failed: Device or resource busy

Find what is holding it:

dmsetup info /dev/vg_data/lv_data | grep -i 'open count'
findmnt /dev/mapper/vg_data-lv_data
lsof /dev/mapper/vg_data-lv_data
fuser -vm /data
ls /sys/block/dm-*/holders/ /sys/block/dm-N/holders/     # stacked devices (LUKS, other LVs, multipath)
dmsetup ls --tree

Common culprits: a mounted filesystem, a process with a working directory inside the mount, a LUKS mapping opened on top, a loop device, a swap area, an exported NFS share, a container with a bind mount, or a kernel snapshot/thin dependency.

I/O errors while removing a volume

If the underlying disk vanished you may see:

/dev/vg_data/lv_data: read failed after 0 of 4096 at 0: Input/output error

Then the VG has a missing PV. Remove the stale mapping and clean up the metadata:

dmsetup remove --force /dev/mapper/vg_data-lv_data
vgreduce --removemissing --force vg_data

16. Configuration (lvm.conf)

Location: /etc/lvm/lvm.conf. View the effective configuration with lvmconfig (lvmconfig --typeconfig diff shows only your changes).

Setting Section Purpose
backup = 1, archive = 1 backup Keep metadata backups and history. Leave enabled.
archive_dir, backup_dir backup Where they are stored.
issue_discards = 1 devices Send TRIM/discard to the disk when an LV is removed or reduced. Good for SSD/thin storage; note that it makes accidental removals unrecoverable.
filter, global_filter devices Regexes controlling which devices LVM scans or ignores. Use global_filter to exclude devices that would cause loops (for example, VM disk images that contain LVM).
use_devicesfile = 1 devices Newer LVM only: restrict scanning to devices listed in /etc/lvm/devices/system.devices. Manage with lvmdevices --adddev /dev/sdb.
thin_pool_autoextend_threshold/percent activation Auto-grow thin pools.
snapshot_autoextend_threshold/percent activation Auto-grow classic snapshots.
monitoring = 1 activation Enable dmeventd monitoring (needed for autoextend and RAID events).
use_lvmetad global Removed in modern releases (autoactivation uses udev / lvm-activate).

After changing filters or on systems that boot from LVM, rebuild the initramfs.


17. Troubleshooting

Symptom Likely cause What to do
Device or resource busy on unmount/remove Open files, mounts, stacked devices Section 15: lsof, fuser -vm, dmsetup ls --tree, check holders
Insufficient free space: N extents needed, but only M available VG has no free PE Add a PV (4.2) or free space by shrinking another LV
resize2fs: New size smaller than minimum Filesystem holds more data than the target Free up data, or choose a larger target
resize2fs: Please run 'e2fsck -f' first Filesystem not freshly checked Run e2fsck -f <dev>, then retry
resize2fs: Invalid new size: 4.5G Fractional units unsupported Use whole units (4G) or M/sectors
xfs_growfs: not a mount point XFS tools need the mount point Mount it and pass the mount path
Trying to shrink XFS Not supported Back up, recreate smaller, restore
LV shows I (invalid) in Attr Snapshot ran out of COW space Remove the snapshot; next time size it larger or enable autoextend
Thin pool at 100% Overcommitted, no autoextend Extend the pool, fstrim, delete unneeded thin volumes; enable autoextend
Couldn't find device with uuid ... PV missing, not decrypted, or not yet scanned Open LUKS, pvscan, check cables; see 13.5 / 13.6
Volume group "X" not found Not scanned or filtered out vgscan, pvscan --cache, check filter/devices file
WARNING: Not using device ... for PV ... because it is a duplicate Multipath or cloned disks Configure multipath properly, or use global_filter
System boots to emergency shell after a rename fstab/initramfs/root= still use old names Fix the names and rebuild the initramfs
LV in use: not deactivating Still mounted or held open Unmount, close LUKS, stop the process using it
Cannot resize ... snapshot exists Some operations refuse with snapshots Remove or merge the snapshot first
I/O errors when accessing a removed disk Missing PV Section 15 / 13.6

General checklist when something looks wrong:

pvs -a; vgs; lvs -a -o +devices
dmsetup ls --tree
journalctl -k -b | grep -Ei 'lvm|device-mapper|dm-'
lvmdiskscan
pvscan --cache && vgscan

18. Best practices

  1. Back up before any shrink, restore, or removal. Snapshots are not backups.
  2. Leave free space in the VG. Unallocated extents give you room to grow any LV, take snapshots, or run pvmove in an emergency. Avoid -l 100%FREE on every LV.
  3. Use meaningful names (vg_prod, lv_pgdata) and avoid hyphens in VG/LV names to sidestep mapper-name doubling.
  4. Prefer -r (--resizefs) on lvextend/lvreduce so the filesystem and LV are always changed together and in the correct order.
  5. Choose XFS when you will only ever grow; choose ext4 if shrinking is a realistic need.
  6. Dry-run with -t/--test and review with -v before destructive commands.
  7. Keep metadata backups enabled and copy /etc/lvm/backup and /etc/lvm/archive (or vgcfgbackup output) off the host regularly.
  8. Do not leave classic snapshots running. Delete them after the backup or merge; monitor Data%.
  9. Monitor thin pools and snapshots (lvs -o +data_percent) and enable dmeventd autoextend.
  10. Separate failure domains. LVM linear/striped LVs across multiple disks fail if any disk fails. Use RAID (LVM RAID or mdadm) under or inside LVM for redundancy.
  11. Use UUIDs or /dev/mapper paths in fstab, not /dev/dm-N, which can change between boots.
  12. Automate fstrim (systemctl enable --now fstrim.timer) for SSDs and thin pools.
  13. Document the layout (lsblk -f, vgdisplay, lvs -a -o +devices output) somewhere off-box before major changes.

19. Cheat sheet

Inspect

pvs; vgs; lvs                 # summaries
pvdisplay; vgdisplay; lvdisplay
lvs -a -o +devices            # which PV backs each LV
lsblk -f                      # full device tree with filesystems
dmsetup ls --tree             # device-mapper dependency tree

Create

pvcreate /dev/sdb1
vgcreate vg_data /dev/sdb1
lvcreate -L 10G -n lv_data vg_data          # or -l 100%FREE
mkfs.xfs /dev/vg_data/lv_data
mount /dev/vg_data/lv_data /data

Grow

lvextend -r -L +5G        /dev/vg_data/lv_data     # add 5G and resize fs
lvextend -r -l +100%FREE  /dev/vg_data/lv_data     # use all free space
vgextend vg_data /dev/sdc                          # add a disk to the VG
pvresize /dev/sdb                                  # after growing an underlying disk
xfs_growfs /data | resize2fs /dev/vg_data/lv_data  # manual fs grow

Shrink (ext4, offline only)

umount /data
e2fsck -f /dev/vg_data/lv_data
lvreduce -r -L 5G /dev/vg_data/lv_data
mount /data

Move / remove disks

pvmove /dev/sdb                # evacuate a PV
vgreduce vg_data /dev/sdb      # remove from the VG
pvremove /dev/sdb              # wipe the label

Snapshots

lvcreate -s -L 5G -n snap vg_data/lv_data      # classic
lvcreate -s -n snap vg_data/lv_thin            # thin (no size)
lvconvert --merge vg_data/snap                 # roll back to the snapshot
lvremove vg_data/snap                          # delete it

Rename / activate / remove

lvrename vg_data lv_old lv_new
vgrename vg_old vg_new
lvchange -an|-ay vg_data/lv_data
vgchange -an|-ay vg_data
lvremove vg_data/lv_data && vgremove vg_data && pvremove /dev/sdb1

Recover

vgcfgrestore -l vg_data                                   # list history
vgcfgrestore --test -f /etc/lvm/archive/<file>.vg vg_data # dry run
vgcfgrestore        -f /etc/lvm/archive/<file>.vg vg_data # restore
vgchange -ay vg_data

External disks

cryptsetup luksOpen /dev/sdx1 ext     # only if encrypted
vgscan && vgchange -ay
mount /dev/<vg>/<lv> /mnt/external
# done:
umount /mnt/external && vgchange -an <vg> && cryptsetup luksClose ext

Further reading