LinuxInfrastructureDevOps

Growing an Ubuntu VM Disk Through LUKS, LVM, and ext4

How I expanded an encrypted Ubuntu VM from its virtual disk through the partition, dm-crypt mapping, LVM physical and logical volumes, and ext4 filesystem.

Diagram of a virtual disk growing upward through a partition, LUKS encryption, LVM, and a filesystem
Lead image Diagram of a virtual disk growing upward through a partition, LUKS encryption, LVM, and a filesystem
On this page

At about 11 p.m., a staging server crossed 82 percent disk usage on / and kept climbing. It was an Ubuntu 24.04 VM running in UTM on a Mac, and a CI pipeline had accumulated enough Docker layers to leave little room on the root filesystem.

I enlarged the VirtIO drive from 100 GB to 150 GB in UTM, restarted the VM, and checked the result over SSH:

df -h /
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv   60G   49G   10G  82% /

The virtual disk was larger, but the filesystem was not. An earlier extension to 100 GB had not produced usable space either. I had changed only the outermost device in a storage stack.

This is the procedure I used to carry that extra capacity through an encrypted LVM installation. The device names and layout are specific to this VM; they are not values to copy without checking your own machine.

Map the storage stack first

The VM had a virtual disk containing a GPT partition. That partition held a LUKS container, whose active dm-crypt mapping was an LVM physical volume. The root logical volume contained an ext4 filesystem:

ext4 filesystem mounted at /
└─ LVM logical volume: ubuntu-lv
   └─ LVM physical volume
      └─ active dm-crypt mapping: dm_crypt-0
         └─ GPT partition: /dev/vda3
            └─ virtual disk: /dev/vda

Growing /dev/vda did not automatically change the partition table, active encryption mapping, LVM metadata, logical volume, or filesystem. Each layer had to see a larger device below it before it could grow.

Before changing those layers, identify the root filesystem and its type, then inspect the block-device tree:

findmnt -no SOURCE,FSTYPE /
lsblk -f
sudo pvs
sudo vgs
sudo lvs

Make a recoverable copy or snapshot before proceeding. Partition and volume commands act on device names, so a typo here is more serious than an ordinary configuration mistake.

The relevant part of lsblk showed the mismatch:

NAME                        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
vda                         253:0    0   150G  0 disk
├─vda1                      253:1    0     1G  0 part  /boot/efi
├─vda2                      253:2    0     2G  0 part  /boot
└─vda3                      253:3    0  60.9G  0 part
  └─dm_crypt-0              252:0    0  60.9G  0 crypt
    └─ubuntu--vg-ubuntu--lv 252:1    0  60.9G  0 lvm   /

The kernel could see the 150 GB virtual disk. The root partition and every layer above it still stopped at about 61 GB.

1. Grow the partition

The root storage lived in partition 3, /dev/vda3. There was free space after it, so I extended that partition as far as the layout allowed:

sudo growpart /dev/vda 3
CHANGED: partition=3 start=6397952 old: size=127817728 end=134215679
                                    new: size=308174815 end=314572766

growpart takes the disk and partition number separately. It moves the partition’s end boundary into available space without moving the start boundary or resizing anything stored inside the partition.

At this point /dev/vda3 was larger, but the active decrypted device was not.

2. Resize the active LUKS mapping

Ubuntu had opened the LUKS container as /dev/mapper/dm_crypt-0. I resized that active mapping to use the newly available space in its underlying partition:

sudo cryptsetup resize dm_crypt-0

On this VM the command asked for the LUKS passphrase. That is not the same as the Linux account password, although another system may retrieve the volume key without prompting.

The distinction here matters: cryptsetup resize changes how much of the underlying device the active dm-crypt mapping represents. It does not resize the raw disk or partition. When I had tried pvresize before this step, LVM correctly reported that its physical volume was already at the maximum size of the smaller decrypted device it could see.

3. Let the LVM physical volume use the space

The dm-crypt mapping was now larger, but LVM still described its physical volume at the old size. I updated it against the enlarged mapping:

sudo pvresize /dev/mapper/dm_crypt-0
Physical volume "/dev/mapper/dm_crypt-0" changed
1 physical volume(s) resized or updated / 0 physical volume(s) not resized

The new capacity became free extents in the ubuntu-vg volume group. At this checkpoint, sudo pvs and sudo vgs should show that the physical volume has grown and the volume group has unallocated space.

4. Extend the root logical volume

I wanted all remaining free extents in this volume group to go to the root logical volume:

sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
Size of logical volume ubuntu-vg/ubuntu-lv changed from <60.93 GiB
  (15598 extents) to <146.93 GiB (37614 extents).
Logical volume ubuntu-vg/ubuntu-lv successfully resized.

+100%FREE consumes all free space in the volume group. That matched this VM, where I wanted all of the space to go to the root logical volume. A fixed increment such as -L +80G would leave room for snapshots or additional logical volumes.

The logical volume was larger now. The ext4 filesystem inside it still retained its previous size.

5. Grow ext4

Because findmnt had identified the root filesystem as ext4, I used resize2fs on the logical volume:

sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
resize2fs 1.47.0 (5-Feb-2023)
Filesystem at /dev/ubuntu-vg/ubuntu-lv is mounted on /; on-line resizing required
old_desc_blocks = 8, new_desc_blocks = 19
The filesystem on /dev/ubuntu-vg/ubuntu-lv is now 38516736 (4k) blocks long.

ext4 supports online expansion, so the root filesystem remained mounted during this operation. This command would not be the right substitution for every filesystem. For example, a mounted XFS filesystem is normally grown by its mount point with xfs_growfs /, not by running resize2fs on its block device.

Verify every layer

To verify the result at every layer, inspect the block-device tree and LVM state again, then ask the mounted filesystem how much space it can use:

lsblk
sudo pvs
sudo vgs
sudo lvs
df -h /

The final filesystem result was:

Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  145G   50G   90G  36% /

The root filesystem had gone from 82 percent usage to 36 percent without being unmounted.

For this VM, the complete growth sequence was:

# /dev/vda3: enlarge the partition within /dev/vda
sudo growpart /dev/vda 3

# dm_crypt-0: enlarge the active mapping over that partition
sudo cryptsetup resize dm_crypt-0

# LVM PV: recognize the larger decrypted device
sudo pvresize /dev/mapper/dm_crypt-0

# LVM LV: allocate the volume group's free extents to root
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv

# ext4: grow the mounted filesystem to the LV's size
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv

An unencrypted installation would not have the dm-crypt step. A system without LVM would not have the physical-volume and logical-volume steps. Different partition layouts and filesystems need their own commands. The useful part of this incident is therefore not a five-command recipe; it is the order: inspect the actual stack, then grow each layer from the virtual disk upward until the mounted filesystem can use the space.