LVM: Explanation & Insights

The flexible layer between your disks and your filesystems, where partitions stop being set in stone.

What It Is

LVM stands for Logical Volume Manager, and the easiest way to understand it is to notice the problem it solves. On a server without it, you slice a disk into partitions once, put a filesystem on each, and then live with those sizes more or less forever. Guessed /var would need 50 GB and it's now choking at 49? Tough. The partition after it on the platter starts exactly where yours ends, so there is nowhere to grow into without a backup, a repartition, and a restore — which on a production box means downtime, a maintenance window, and a knot in your stomach.

LVM removes the rigidity by sliding a layer of indirection between the physical disks and the filesystems that sit on top. Instead of formatting a partition directly, you hand your disks to LVM, LVM pools them, and you carve logical volumes out of that pool — virtual partitions that can be grown, shrunk, snapshotted, and even moved to entirely different hardware while the server keeps serving. The filesystem never knows the difference. It thinks it's sitting on an ordinary block device, because as far as it can tell, it is.

That one idea — don't let the filesystem touch the metal directly — is the whole of LVM, and the rest of this page is about why it's worth the small amount of extra ceremony. We'll build the three-tier model from the ground up, walk through creating volumes, grow a live filesystem end to end, look at snapshots and thin provisioning (the two features that earn LVM its keep and the two that bite hardest when misused), and see how the whole thing rests on the kernel's device-mapper.

Why It Matters

Storage requirements on a server are a moving target. The database that started as a hobby table grows into a hundred gigabytes; the log directory that nobody worried about fills up at 2 a.m.; a new service needs its own volume yesterday. Without LVM, every one of these is a partition-table surgery. With it, most are a single command you can run on a live system, with users connected and queries flowing.

The three things LVM buys you, in rough order of how often you'll be grateful for them:

  1. Grow on demand, online. Add space to a volume and extend its filesystem while it's mounted and busy. No reboot, no unmount, no downtime. This alone justifies LVM on most servers.
  2. Span multiple disks transparently. A single logical volume can stretch across several physical disks, so you're no longer capped at the size of your biggest drive. Run out of room? Add a disk to the pool and grow into it.
  3. Snapshots and live migration. Freeze a consistent point-in-time view for a backup, or evacuate a dying disk with pvmove while everything stays online.

My standing advice: on any real server, put your filesystems on LVM by default. The overhead is negligible, the flexibility is enormous, and the one time you need to grow /var at 2 a.m. without a maintenance window, you'll consider it the best ten minutes of setup you ever did. The exceptions are narrow — a tiny single-purpose VM you'll rebuild rather than resize, or a /boot partition (the bootloader wants something it can read before LVM exists). Everything else: LVM.

The Three-Tier Model

This is the heart of LVM, and if you take one thing from this page, take this. The whole system is three layers stacked on top of each other, each feeding the one above. Learn the stack and every command afterwards is obvious.

  Logical Volumes   (LV)   lv_root   lv_var    lv_data     ← you format & mount these
  ──────────────────────────────────────────────────────
  Volume Group      (VG)         one big pool of space
  ──────────────────────────────────────────────────────
  Physical Volumes  (PV)   /dev/sda2   /dev/sdb1  /dev/sdc  ← real disks/partitions

Physical Volume (PV) — The Raw Material

A physical volume is a real block device you've handed over to LVM: a whole disk (/dev/sdb), a partition (/dev/sda2), or even a RAID array (/dev/md0). You initialise it with pvcreate, which writes a small LVM label and metadata header at the start of the device so LVM can recognise it later. That's all a PV is — a chunk of storage with a name tag that says "I belong to LVM now."

pvcreate /dev/sdb1

Under the hood, LVM chops every PV into equal-sized chunks called physical extents (PEs), 4 MB each by default. Extents are the bricks LVM actually shuffles around; when you grow a volume or run pvmove, you're really moving extents. You rarely think about them directly, but they're why LVM can do its tricks — everything is allocated in these uniform little blocks, so any extent can live on any PV.

Volume Group (VG) — The Pool

A volume group is one or more PVs glued into a single pool of space. This is the abstraction that breaks the one-disk-one-filesystem tyranny: once /dev/sdb1 and /dev/sdc1 are both in the same VG, their capacity is fungible. LVM no longer cares which physical disk a given byte lives on — it just sees a flat pool of extents to hand out.

vgcreate vg_data /dev/sdb1 /dev/sdc1

Now vg_data holds the combined space of both disks. Need more later? vgextend vg_data /dev/sdd1 folds a third disk into the same pool, and every logical volume in it can immediately grow into the new room.

Logical Volume (LV) — What You Actually Use

A logical volume is a slice carved out of a VG's pool — and it's the thing you format with a filesystem and mount. To the rest of the system it looks exactly like a partition, appearing as a device under /dev/<vg>/<lv> (and /dev/mapper/<vg>-<lv>).

lvcreate -L 20G -n lv_data vg_data
mkfs.ext4 /dev/vg_data/lv_data
mount /dev/vg_data/lv_data /data

The LV doesn't have to be one contiguous run on one disk. LVM can scatter its extents across every PV in the group, which is exactly how a single volume spans multiple disks. The filesystem on top sees a smooth, continuous block device and is none the wiser about the patchwork of extents underneath.

Note

A clean naming convention saves you grief later. Many admins prefix volume groups with vg_ and logical volumes with lv_ (vg_data, lv_var), so a glance at any /dev/mapper/ entry instantly tells you which is the pool and which is the slice. LVM doesn't enforce it, but future-you will be grateful.

Why the Indirection Is the Whole Point

Here's the part that makes LVM click, and it's worth slowing down for. Because your filesystem sits on a logical volume rather than a raw partition, the layer underneath it is now soft. The filesystem is pinned to a name (/dev/vg_data/lv_data), not to a physical location on a platter — and LVM is free to redefine what that name maps to without telling the filesystem anything.

Think about what that unlocks. On a raw partition, the partition after yours begins at a fixed sector, so growing means moving someone else's data first. On an LV, "grow" just means "assign a few more extents from the pool to this volume" — extents that can come from anywhere, including a disk you plugged in five minutes ago. The filesystem then stretches into the new space, live. There was never any neighbour to evict because there is no fixed neighbour; the pool is just a bag of interchangeable bricks.

The same indirection is what makes pvmove feel like sorcery. You can tell LVM "drain every extent off /dev/sdb, this disk is dying," and it copies them, one at a time, onto the remaining PVs while the volume stays mounted and the application keeps writing. The LV's name never changes, so nothing above it notices its data is quietly relocating beneath its feet. When the last extent has moved, you vgreduce the dead disk out and pull it. Zero downtime, on a disk you were about to lose. That is the indirection paying rent.

The Grow Workflow, End to End

This is the move you'll reach for most, so here it is start to finish. Say /data is filling up and you've got free space left in the volume group. One command does both halves — extend the LV and resize the filesystem to match:

# Confirm there's free space in the pool first
vgs                                       # look at the VFree column

# Grow the LV by 10 GiB AND resize the filesystem in one shot
lvextend -r -L +10G /dev/vg_data/lv_data

The magic is the -r flag (--resizefs). Without it, lvextend would grow only the logical volume — the container — leaving the filesystem inside still convinced it's the old size, so you'd see no extra space until you also ran resize2fs (for ext4) or xfs_growfs (for XFS). With -r, LVM calls the right resize tool for you. Both of those filesystems grow happily while mounted, so the whole thing happens live, on a busy server, in seconds. No window, no unmount, no drama.

You can also grow to an absolute size (-L 50G) or fill all remaining free space (-l +100%FREE), which is the lazy-but-honest move when you simply want a volume to eat the rest of the pool.

Danger

Growing is safe; shrinking is where data dies, and the order of operations is non-negotiable. To grow, you extend the LV first, then the filesystem. To shrink, you must reverse it: shrink the filesystem first, then the LV — because if you shrink the volume before shrinking the filesystem, you've just lopped off the tail of a filesystem that still believes it owns that space, and the data living there is gone. Worse, XFS cannot shrink at all, so on an XFS volume "shrink the LV" is simply never an option. Unmount, run resize2fs to the smaller size, then lvreduce. Back up first. Measure twice.

Snapshots

A snapshot is a frozen, point-in-time view of a logical volume, and it's one of LVM's quietly brilliant features. The instant you take one, you have a second volume that looks exactly like the original did at that moment — perfect for taking a consistent backup of a busy database without stopping it, or for grabbing a safety net right before a risky upgrade.

lvcreate -L 5G -s -n snap_data /dev/vg_data/lv_data

The trick is copy-on-write (CoW). The snapshot doesn't duplicate 20 GB of data up front — that would be slow and wasteful. Instead it starts nearly empty and shares all the original blocks. Only when a block on the original volume is about to change does LVM first copy the old version into the snapshot's reserved space, then let the write proceed. The snapshot accumulates only the differences, which is why a snapshot of a mostly-idle volume costs almost nothing, and a snapshot of a write-heavy volume can balloon fast.

Warning

A snapshot has a fixed size (the -L 5G above), and if it fills up — if the original volume changes more than the snapshot can hold the old copies of — the snapshot is dropped and becomes invalid. Your live data is fine; the snapshot just vanishes, taking your half-finished backup with it. Size snapshots for the write churn you expect during their lifetime, keep them short-lived (delete the moment the backup is done), and watch their fill level with lvs — the Data% column on a snapshot is a fuse burning down.

Thin Provisioning

Classic LVM volumes are thick: carve a 20 GB LV and it reserves 20 GB from the pool immediately, used or not. Thin provisioning flips this. You create a thin pool, then create thin volumes inside it that allocate space lazily — a "100 GB" thin volume that holds 3 GB of real data consumes only 3 GB of the pool. Space is handed out on first write, not on creation.

lvcreate -L 100G -T vg_data/thinpool          # a 100 GB thin pool
lvcreate -V 50G -T vg_data/thinpool -n lv_a   # a "50 GB" thin volume
lvcreate -V 50G -T vg_data/thinpool -n lv_b   # another "50 GB" thin volume
lvcreate -V 50G -T vg_data/thinpool -n lv_c   # ...and a third

Notice the arithmetic above: three 50 GB volumes on a 100 GB pool. That's 150 GB promised out of 100 GB that exists — overcommit, and it's the entire point of thin provisioning. It's the airline model: not every volume uses all its space at once, so you can sell more seats than the plane has and usually get away with it. You get density, flexibility, and snapshots that are first-class instead of fixed-size.

Warning

Overcommit means the pool can run out of real space while the thin volumes still think they have room. When a thin pool fills, there's nowhere to put the next write — and every thin volume on that pool goes read-only or starts throwing I/O errors at the same instant. Not one volume: all of them, together, the moment the shared pool hits 100%. This is the foot-gun that turns "we'll just overcommit a little" into a multi-service outage. If you thin-provision, you must monitor the pool's Data% and metadata usage and either extend the pool or reclaim space well before it fills. Treat a thin pool the way you'd treat an overbooked flight — fine until everyone shows up.

How I Inspect It

The LVM toolkit is wonderfully consistent: there's a *create, a *display, and a one-letter summary command at each tier. The summary trio — pvs, vgs, lvs — is where I always start, because each prints one tidy line per object.

pvs        # one line per physical volume
vgs        # one line per volume group
lvs        # one line per logical volume
# pvs
  PV         VG       Fmt  Attr PSize    PFree
  /dev/sdb1  vg_data  lvm2 a--  <500.00g  20.00g
  /dev/sdc1  vg_data  lvm2 a--  <500.00g   0

# vgs
  VG       #PV #LV #SN Attr   VSize  VFree
  vg_data    2   3   0 wz--n- <1.00t 20.00g

# lvs
  LV        VG       Attr       LSize   Pool     Data%
  lv_data   vg_data  -wi-ao---- 480.00g
  snap_data vg_data  swi-a-s--- 5.00g    lv_data  42.31

Read these top-down to answer the obvious questions. vgs shows VFree — how much room is left in the pool to grow into. lvs shows the volumes and, for snapshots and thin volumes, the all-important Data% (here the snapshot snap_data is 42% full — plenty of headroom). pvs shows PFree per disk, which tells you which physical disk still has unallocated extents.

When I need the full story on one object rather than the summary, I reach for the *display commands:

lvdisplay /dev/vg_data/lv_data    # everything about one logical volume
vgdisplay vg_data                 # full detail on a volume group
pvdisplay /dev/sdb1               # full detail on a physical volume

lvdisplay is the one I open most — it spells out the LV's size, its UUID, its current device-mapper path, and (for snapshots) the source volume and CoW table status. And of course lsblk ties it all together visually, showing the disk → PV → LV → mountpoint tree in one tree-shaped glance — the single best way to see how the layers nest on a box you've just inherited.

Cheat Sheet

The commands you'll actually use, grouped by tier and task. Notice the pattern: pv* for disks, vg* for pools, lv* for volumes.

# --- Inspect (start here) ---
pvs ; vgs ; lvs                              # one-line summaries at each tier
lvdisplay /dev/vg_data/lv_data               # full detail on one LV
lsblk                                        # disk → PV → LV → mountpoint tree

# --- Build the stack (bottom-up) ---
pvcreate /dev/sdb1                           # initialise a disk/partition as a PV
vgcreate vg_data /dev/sdb1 /dev/sdc1         # pool one or more PVs into a VG
lvcreate -L 20G -n lv_data vg_data           # carve an LV out of the pool
lvcreate -l +100%FREE -n lv_data vg_data     # ...or take all remaining space
mkfs.ext4 /dev/vg_data/lv_data               # format it like any block device

# --- Grow (online, the everyday move) ---
vgs                                          # check VFree before you start
lvextend -r -L +10G /dev/vg_data/lv_data     # grow LV + filesystem in one shot
lvextend -r -l +100%FREE /dev/vg_data/lv_data# grow to fill the pool

# --- Add capacity to the pool ---
pvcreate /dev/sdd1                           # prep a new disk
vgextend vg_data /dev/sdd1                   # fold it into the pool

# --- Shrink (filesystem FIRST — unmount, ext only, never XFS) ---
umount /data
resize2fs /dev/vg_data/lv_data 100G          # shrink the filesystem first
lvreduce -L 100G /dev/vg_data/lv_data        # then shrink the LV

# --- Move data off a dying disk, live ---
pvmove /dev/sdb1                             # drain all extents off this PV
vgreduce vg_data /dev/sdb1                   # remove the empty PV from the pool

# --- Snapshots ---
lvcreate -L 5G -s -n snap_data /dev/vg_data/lv_data   # take a CoW snapshot
lvremove /dev/vg_data/snap_data              # drop it when the backup is done

# --- Thin provisioning ---
lvcreate -L 100G -T vg_data/thinpool         # create a thin pool
lvcreate -V 50G -T vg_data/thinpool -n lv_a  # thin volume (lazy allocation)
lvs -o +data_percent,metadata_percent        # WATCH the pool fill level

Pro Tip

When you create a volume group, hand it whole partitions or disks, not the same disk twice, and let LVM manage the layout. The one habit worth forming early: before any grow, run vgs and read the VFree column. Half the "I ran lvextend and it failed" confusion is simply a pool with no free extents left — the fix is vgextend with another disk, not a different flag.

Built on Device-Mapper

LVM doesn't actually move your bytes around. The real engine is device-mapper, a kernel framework that builds virtual block devices by mapping ranges of a logical device onto ranges of physical ones. When you lvcreate, the LVM userspace tools work out which extents on which PVs your volume should use, then hand device-mapper a table describing that mapping. From then on, every read and write to /dev/vg_data/lv_data is the kernel consulting that table and redirecting the I/O to the right offset on the right physical disk.

This is why the device path is /dev/mapper/vg_data-lv_data: the mapper directory is device-mapper's, not LVM's. LVM is best understood as a friendly management layer that speaks device-mapper on your behalf — it remembers your volume groups, stores metadata on the disks, and translates "grow this by 10 GB" into a fresh device-mapper table. The same framework underpins disk encryption (LUKS), thin pools, software RAID targets, and multipath. LVM was simply its first and most popular tenant.

The lovely consequence: because the mapping is just a kernel table, redefining it is instant. Growing a volume isn't copying anything — it's editing a table to say "this device is now bigger." That's the secret behind LVM's online tricks. The data never moves (except in pvmove, which genuinely does relocate extents); only the map changes.

Gotchas

Things that trip people up, roughly in order of how often they cost an afternoon:

  • lvextend without -r grows the volume but not the filesystem. You extend the LV, run df, and see no extra space — because the filesystem inside still thinks it's the old size. Either always pass -r, or follow up with resize2fs (ext4) / xfs_growfs (XFS). This is the single most common LVM "huh?" moment.
  • Shrinking in the wrong order destroys data. Filesystem first, then the volume — always. And remember XFS can't shrink at all. See the Danger box above; it's worth re-reading before you ever type lvreduce.
  • A full thin pool takes down every volume on it at once. Overcommit is a promise you have to keep watching. If the pool's Data% or metadata hits 100%, everything on it goes read-only together. Monitor it, and extend the pool before it fills.
  • A full snapshot silently invalidates itself. Undersize a snapshot, let the source churn past it, and the snapshot is dropped — your backup job fails halfway with no warning beyond the snapshot disappearing. Size for the churn and keep snapshots short-lived.
  • Don't forget /boot lives outside LVM. The bootloader has to find the kernel before LVM is initialised, so /boot is almost always a plain partition. Putting it inside an LV is a classic way to build a server that won't boot.
  • A missing PV can lock up a whole VG. If a disk in a volume group dies and you had no redundancy underneath (LVM isn't RAID — see below), the volumes that had extents on it are damaged. Put LVM on top of RAID when you want both flexibility and resilience: RAID handles the dead disk, LVM handles the flexible carving above it.

Backstory

People sometimes ask LVM to be their RAID too — it does offer lvcreate --type raid1 mirroring and striping. It works, but the common and battle-tested pattern is to layer them: build a RAID array with mdadm for redundancy, pvcreate that array as a single PV, and run LVM on top for the flexible volume carving. Redundancy underneath, flexibility above — each tool doing the one thing it's best at.

History and Philosophy

LVM is one of those Linux subsystems that quietly borrowed a great idea from the big-iron Unix world and made it free. Logical volume management arrived first on commercial Unixes — HP-UX shipped it in the early 1990s, and IBM's AIX had its own — where storage was expensive, downtime was unthinkable, and the ability to reshape volumes on a running system was worth real money. Linux's LVM, written by Heinz Mauelshagen and merged into the 2.4 kernel around 2000, brought the same superpower to commodity servers. LVM2, the version everyone runs today, rebuilt it on top of device-mapper — cleanly separating the policy (LVM's userspace tools and metadata) from the mechanism (the kernel's mapping engine).

That separation is the philosophy worth remembering. LVM's job is not to touch your data; it's to manage a map. Disks change, requirements change, hardware dies and gets replaced — and through all of it, the names your filesystems mount stay constant while the storage beneath them is quietly re-plumbed. It's the same instinct as a DNS name in front of a fleet of servers, or a load balancer in front of a pool of backends: put a stable name in front of a shifting reality, and you can rearrange the reality without anyone upstream noticing. LVM is that principle applied to the slab of metal under your filesystem — and once you've grown a full volume on a live server with one command, you'll never want to format a bare partition again.

See Also

  • device-mapper — the kernel engine LVM steers; the real machinery behind every logical volume
  • partition — the rigid old way to slice a disk, and what LVM frees you from
  • filesystem — what you actually format and mount on top of a logical volume
  • block device — the abstraction an LV presents, indistinguishable from a real partition
  • xfs — grows online with lvextend -r, but never shrinks; plan accordingly
  • ext4 — grows and shrinks; the safe default to put on a logical volume
  • RAID — the redundancy layer that belongs underneath LVM, not instead of it
  • disk — the physical hardware that becomes a physical volume
  • lsblk — the clearest view of the disk → PV → LV → mountpoint stack
  • df — check usage on the filesystems your logical volumes carry
  • disk full — the emergency LVM's online grow is built to solve

Need to grow a volume before it fills — without a 2 a.m. maintenance window?

CleverUptime watches every mounted filesystem on your server and warns you when one is trending toward full, so you can extend the volume calmly during the day instead of scrambling after it's already wedged.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →