Resizing a Linux Filesystem Live with LVM (No Downtime)
The disk is full and the app is still running. Here is how to grow a logical volume and its filesystem underneath live workloads, safely.
Key takeaways
- The disk is full and the app is still running.
- Here is how to grow a logical volume and its filesystem underneath live workloads, safely.
On this page
Resizing a Linux Filesystem Live with LVM (No Downtime)
df -h shows 100% and the app is still serving traffic. If the disk sits on LVM, you can almost always grow it without stopping anything: add space to the volume group, extend the logical volume, then grow the filesystem on top. The three steps are independent operations and mixing up their order is the most common way this goes wrong, so this walks through each one and what confirms it actually worked before moving to the next.
Check what you're actually working with first#
$ df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg_data-lv_data 50G 50G 0 100% /data
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 99G 0 part
└─vg_data-lv_data 253:0 0 50G 0 lvm /data
$ vgs
VG #PV #LV #SN Attr VSize VFree
vg_data 1 1 0 wz--n- 99.00g 49.00g
That last line is the answer to "do I even need a new disk": VFree shows 49G already sitting unused in the volume group. Nothing needs to be attached. The space is there, it's just not allocated to the logical volume yet.
If VFree were closer to 0, you'd attach a new disk or cloud volume, then bring it into the volume group before continuing:
$ sudo pvcreate /dev/sdb
$ sudo vgextend vg_data /dev/sdb
Extend the logical volume#
$ sudo lvextend -L +40G /dev/vg_data/lv_data
Size of logical volume vg_data/lv_data changed from 50.00 GiB to 90.00 GiB.
Logical volume vg_data/lv_data successfully resized.
-L +40G adds 40G to the current size. To use everything free in the volume group instead of a fixed number, use -l +100%FREE:
$ sudo lvextend -l +100%FREE /dev/vg_data/lv_data
At this point the block device is bigger, but the filesystem sitting on it has no idea; df still reports the old size. This is the step people stop at and then wonder why nothing changed.
Grow the filesystem to match#
The command depends on the filesystem type, and both of these work on a mounted, live filesystem:
# ext4
$ sudo resize2fs /dev/vg_data/lv_data
Filesystem at /dev/vg_data/lv_data is mounted on /data; on-line resizing required
resize2fs 1.46.5 (30-Dec-2021)
The filesystem on /dev/vg_data/lv_data is now 23592960 (4k) blocks long.
# XFS note: resize takes the *mount point*, not the block device
$ sudo xfs_growfs /data
That distinction trips people up constantly: resize2fs targets the device, xfs_growfs targets the mount point. Running xfs_growfs against a device path fails immediately, which is a useful sanity check that you've got the right tool for the filesystem in front of you.
Confirm before you move on#
$ df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg_data-lv_data 89G 50G 35G 57% /data
Don't trust the previous step's "success" output alone. df on the live mount is the real confirmation. If it still shows the old size after resize2fs/xfs_growfs reported success, the mount you resized and the mount you're checking may not be the same device (common with bind mounts or a stale NFS cache); recheck lsblk and mount | grep /data before doing anything else.
Shrinking is a different, riskier operation#
Everything above only grows. Shrinking works in the opposite order (filesystem first, then logical volume), and it is much easier to lose data if you get it wrong:
- XFS cannot be shrunk at all, online or offline. The only path is backup, recreate, restore.
- ext4 can be shrunk, but only unmounted, and only with
resize2fsgiven an explicit target size smaller than the current filesystem, run beforelvreduce, never after. Shrinking the logical volume before the filesystem has been told to release that space truncates live data.
# ext4 shrink: offline, filesystem first, LV second, in that order
$ sudo umount /data
$ sudo e2fsck -f /dev/vg_data/lv_data
$ sudo resize2fs /dev/vg_data/lv_data 40G
$ sudo lvreduce -L 40G /dev/vg_data/lv_data
$ sudo mount /data
If a filesystem needs to shrink and it's XFS, there usually isn't a clean answer short of migrating the data to a smaller volume. Plan for that with headroom rather than discovering it during an incident.
Snapshot before anything that isn't a pure grow#
LVM snapshots are cheap and this is exactly the scenario they exist for:
$ sudo lvcreate -L 5G -s -n lv_data_snap /dev/vg_data/lv_data
$ # ... do the risky operation ...
$ sudo lvremove /dev/vg_data/lv_data_snap # once confirmed good
A snapshot isn't a substitute for a real backup (it lives on the same volume group and disappears if the underlying storage fails), but it's a fast rollback path for "this shrink or filesystem operation went wrong five minutes ago," which covers most of the actual risk in these commands.
The fix in order#
- Check
vgsfor free space before touching anything; you may not need a new disk at all. - Grow:
lvextendon the logical volume, thenresize2fs(ext4) orxfs_growfs(XFS, mount point not device) on the filesystem. Both work live. - Confirm with
df -hon the actual mount, not just the command's own success message. - Shrink (ext4 only, XFS can't): unmount,
resize2fsto the smaller target first,lvreducesecond, since reversing that order risks data loss. - Snapshot before any shrink or filesystem-type-specific operation you haven't run before on this box.
This pairs with diagnosing disk full on Linux when the question is what's eating the space in the first place, not just how to add more of it.
The call we'd make#
Keep 15-20% headroom in the volume group on any disk-sensitive box so a grow is always the lvextend + resize path above: five minutes, zero downtime, reversible via snapshot if something looks wrong. Treat shrinking as a planned migration, not a live operation, especially on XFS where it's not an option at all.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
Broken Access Control and IDOR Explained (OWASP #1)
A practitioner's guide to broken access control and IDOR, why scanners miss them, and how to authorize every request correctly.
Insecure Deserialization: Risks and How to Prevent It
Insecure deserialization lets attackers turn untrusted data into arbitrary code execution, and here's how it happens and how to stop it.
More from Linux
Explore more articles in this category
ext4 vs XFS vs Btrfs: Choosing a Filesystem for a Server
The default filesystem your distro picks is not always the right one for your workload. Here is what actually differs and when each one wins.
journald Log Management: Retention, Filtering, and Forwarding
journald is the default log sink on every systemd distro, and most of it runs on defaults nobody chose. Here is how to actually control it.
DNS Troubleshooting on Linux: A Systematic Approach
\"It's always DNS\" is a joke because the failure modes are so scattered: resolver config, caching, search domains, split DNS. Here is where to actually look.
You might have missed
Evergreen posts worth revisiting.