Growing LVs and LVM snapshots
Growing an LV online requires lvextend (to grow the block device) then resize2fs or xfs_growfs (to grow the filesystem) — order matters. LVM snapshots are copy-on-write point-in-time views ideal for consistent backups. A snapshot that fills up silently becomes invalid.
Disk usage on /data hits 90%. With a raw partition you would need to unmount, boot a live disk, use parted to shrink the neighbour partition and grow this one, then resize the filesystem — an offline procedure with multiple ways to destroy data. With LVM you run two commands while the filesystem is mounted and traffic is live.
But there is a catch most documentation glosses over: the order is non-negotiable. Growing the filesystem before the block device is a no-op at best, a corruption at worst if you later shrink. Growing the block device without growing the filesystem wastes the space silently. Get the order right and LVM online resize is one of the safest operations you will do.
After this lesson you can grow an LVM logical volume and its filesystem online, explain why the order (lvextend then resize) is mandatory, create and use LVM snapshots for consistent backups, and understand when a snapshot becomes invalid.
First, check how much space is available in the VG.
Before extending an LV you need to know the VG has free extents. LV space comes from the VG pool — if the VG is full you must either add a PV (vgextend) or shrink another LV first.
# How much is free in the VG?
sudo vgs
# VG #PV #LV #SN Attr VSize VFree
# datavg 2 2 0 wz--n- <99.99g 30.00g
# 30 GB free — safe to extend
# More detail: free physical extents
sudo vgdisplay datavg | grep -E 'Free|Total|Alloc'
# Total PE 25599
# Alloc PE / Size 18176 / 71.00 GiB
# Free PE / Size 7423 / 29.00 GiB
# Which LV do we want to grow?
sudo lvs
# LV VG Attr LSize
# appdata datavg -wi-ao---- 30.00g
# logs datavg -wi-ao---- 41.00g
# Current filesystem usage (what prompted the alarm)
df -h /mnt/appdata
# Filesystem Size Used Avail Use%
# /dev/mapper/datavg-appdata 30G 27G 1.4G 95%Step 1 of growing: extend the logical volume (the block device layer).
# Extend appdata LV by 20 GB (adds to current size)
sudo lvextend -L +20G /dev/datavg/appdata
# Size of logical volume datavg/appdata changed from 30.00 GiB to 50.00 GiB.
# Logical volume datavg/appdata successfully resized.
# Or extend to a specific total size
sudo lvextend -L 50G /dev/datavg/appdata
# Or consume all remaining free space in the VG
sudo lvextend -l +100%FREE /dev/datavg/appdata
# After lvextend, the block device is larger but the FILESYSTEM does not know yet:
df -h /mnt/appdata
# Filesystem Size Used Avail Use%
# /dev/mapper/datavg-appdata 30G 27G 1.4G 95%
# <-- still shows 30 GB! The filesystem has not been told about the extra space.lvextend only grows the block device. The filesystem sits on top and has its own size metadata — you must resize it separately.
Step 2 of growing: resize the filesystem to fill the LV.
The command differs by filesystem type:
# For ext4: resize2fs (can run on a live, mounted filesystem)
sudo resize2fs /dev/datavg/appdata
# resize2fs 1.47.0 (5-Feb-2023)
# Filesystem at /dev/datavg/appdata is mounted on /mnt/appdata; on-line resizing required
# old_desc_blocks = 4, new_desc_blocks = 7
# The filesystem on /dev/datavg/appdata is now 13107200 (4k) blocks long.
# For xfs: xfs_growfs (requires mount point, NOT device path)
sudo xfs_growfs /mnt/appdata
# meta-data=/dev/mapper/datavg-appdata isize=512 agcount=4, agsize=1966080 blks
# data = bsize=4096 blocks=7864320, imaxpct=25
# ...
# data blocks changed from 7864320 to 13107200
# Verify the filesystem is now larger
df -h /mnt/appdata
# Filesystem Size Used Avail Use%
# /dev/mapper/datavg-appdata 50G 27G 21G 57%
# <-- now shows 50 GBThe mandatory order: lvextend first, then filesystem resize. If you try to resize2fs on a device that is smaller than the filesystem expects, it errors. If you run lvextend but forget resize2fs, the filesystem continues using the old size — the extra space exists but is invisible to the OS.
Shortcut: lvextend -r (resize flag) tells LVM to run resize2fs or xfs_growfs automatically after extending:
sudo lvextend -L +20G -r /dev/datavg/appdata
# Extends LV and resizes filesystem in one commandLVM snapshots: copy-on-write point-in-time views.
An LVM snapshot creates a point-in-time view of a logical volume. It does not copy the data immediately — instead it uses copy-on-write (COW): blocks are copied to the snapshot only when the original LV changes them. This makes snapshot creation instantaneous.
# Create a 5 GB snapshot of appdata (the COW buffer)
# The snapshot size is NOT the size of the data — it is how much change you expect during backup
sudo lvcreate -s -L 5G -n appdata-snap /dev/datavg/appdata
# Logical volume "appdata-snap" created.
sudo lvs
# LV VG Attr LSize Origin Snap% Move Log
# appdata datavg owi-aos--- 50.00g
# appdata-snap datavg swi-a-s--- 5.00g appdata 0.00
# 'o' = origin, 's' = snapshot, Snap% = how full the COW buffer is
# Mount the snapshot read-only for backup
sudo mkdir /mnt/snap
sudo mount -o ro /dev/datavg/appdata-snap /mnt/snap
# Run backup against the consistent snapshot (original LV keeps serving traffic)
sudo rsync -av /mnt/snap/ /backup/appdata-$(date +%Y%m%d)/
# Clean up: unmount and remove the snapshot
sudo umount /mnt/snap
sudo lvremove /dev/datavg/appdata-snapThe snapshot fill trap: a full snapshot becomes invalid.
The COW buffer (the size you gave to lvcreate -s) must absorb every block that changes on the origin LV between snapshot creation and removal. If more data changes than the buffer can hold, the snapshot fills up and becomes invalid.
# Watch snapshot utilisation — monitor this during a long backup
sudo lvs /dev/datavg/appdata-snap
# LV Snap%
# appdata-snap 73.41 <-- 73% used, still safe
# If it hits 100%:
# appdata-snap 100.00 <-- INVALID — data is gone from the snapshot
# Any attempt to read from it will return I/O errors
# Set a monitoring threshold: warn at 80%
# LVM can auto-extend snapshots (add to /etc/lvm/lvm.conf):
# snapshot_autoextend_threshold = 80
# snapshot_autoextend_percent = 20
# (LVM daemon lvmetad/lvmpolld will then extend automatically)
# Size the COW buffer conservatively:
# - For a busy database during a long backup: 20-30% of origin size
# - For a quiet filesystem during a quick rsync: 5-10% is fine
# - When in doubt, bigger is safer — unused COW space is wasted but not harmfulProduction scenario: growing a PostgreSQL data volume without downtime.
# Situation: /dev/pgvg/pgdata is mounted at /var/lib/postgresql
# df shows 92% usage — need to add 50 GB now, PostgreSQL keeps running
# Step 1: confirm VG has space
sudo vgs pgvg
# VG #PV #LV VSize VFree
# pgvg 1 1 200.00g 100.00g
# 100 GB free — good
# Step 2: determine filesystem type (matters for the resize command)
findmnt /var/lib/postgresql
# TARGET SOURCE FSTYPE
# /var/lib/postgresql /dev/mapper/pgvg-pgdata ext4
# Step 3: extend LV and filesystem in one shot (-r flag)
sudo lvextend -L +50G -r /dev/pgvg/pgdata
# Extending logical volume pgvg/pgdata to 150.00 GiB
# resize2fs 1.47.0: Filesystem at /dev/pgvg/pgdata is mounted on /var/lib/postgresql
# The filesystem on /dev/pgvg/pgdata is now 39321600 (4k) blocks long.
# PostgreSQL never paused. Verify:
df -h /var/lib/postgresql
# Filesystem Size Used Avail Use%
# /dev/mapper/pgvg-pgdata 150G 92G 53G 64%
# Step 4 (good practice): take a snapshot before the next major schema migration
sudo lvcreate -s -L 20G -n pgdata-premigration /dev/pgvg/pgdata
# Logical volume "pgdata-premigration" created.
# Run the migration. If it goes wrong:
# sudo lvconvert --merge /dev/pgvg/pgdata-premigration
# (merges the snapshot back — effectively a rollback; requires stop + restart of the LV)
# When migration succeeds, drop the snapshot:
sudo lvremove /dev/pgvg/pgdata-premigration▸Common mistake
The most dangerous mistake with LVM snapshots: assuming a snapshot is indefinitely safe once created. If the origin LV has heavy write traffic and you run a slow backup, the COW buffer drains fast. At 100% full the snapshot silently becomes invalid — reads from the snapshot return I/O errors, not a clear error message. Always size the COW buffer generously for production volumes, monitor Snap% in lvs during the backup, and configure snapshot_autoextend_threshold in /etc/lvm/lvm.conf so LVM extends the buffer automatically before it fills.
▸Why this works
Why does xfs_growfs take a mount point while resize2fs takes a device path? XFS locks its metadata through the VFS (virtual filesystem switch) layer and requires the live mount context to issue the grow ioctl. ext4’s resize2fs works at the block level and does not need the mount point — though for a live filesystem it still uses an ioctl internally. The practical consequence: for XFS always use the mount path (/mnt/appdata), never the device; for ext4 you can use either, though the device path is more common in documentation.
You run 'sudo lvextend -L +30G /dev/myvg/data' and it succeeds. Then 'df -h /data' still shows the old smaller size. What must you do, and why did lvextend not fix it?
Growing an LV online requires two steps in order: lvextend first (grows the block device, allocating extents from the VG’s free pool), then resize2fs /dev/vg/lv for ext4 or xfs_growfs /mountpoint for XFS (tells the filesystem to fill the new space). The -r flag on lvextend does both in one command. LVM snapshots are copy-on-write point-in-time views created with lvcreate -s; they capture the state of an origin LV instantly and let you run a backup against the snapshot while the origin keeps serving writes. The snapshot COW buffer absorbs every block changed on the origin since creation — if it fills to 100%, the snapshot silently becomes invalid. Size the buffer generously, monitor Snap% in lvs, and configure snapshot_autoextend_threshold in /etc/lvm/lvm.conf for production volumes.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.