Live increasing a KVM VM’s root partition

My mailserver’s root partition has gotten really full lately, and I didn’t want to incur downtime by taking it down to enlarge offline:

root@mail:~# df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/vda2      ext4       19G   17G  1.2G  94% /

I run my VMs on KVM and I use an LVM LV for each VM, and according to a SO post, it is possible to increase the size partitions within a VM online, so let’s go for it:

Firstly, ensure there is enough free VG space to make room for the slice and check its current size:

root@fireball:~# vgs
   VG   #PV #LV #SN Attr   VSize   VFree  
   vg1    2  16   0 wz--n- 836.74g 143.74g

Next, increase the LV:

root@fireball:~# lvresize -L +10G /dev/vg1/vm-mail 
  Size of logical volume vg1/vm-mail changed from 20.00 GiB (5120 extents) to 30.00 GiB (7680 extents).
  Logical volume vm-mail successfully resized.

Now that the LV is physically larger, we need to make KVM notify the guest that its disk has increased. First, get the virtio identifier:

root@fireball:~# virsh qemu-monitor-command mail info block --hmp
drive-virtio-disk0: /dev/vg1/vm-mail (raw)

Next, use that identifier like this:

root@fireball:~# virsh qemu-monitor-command  mail block_resize drive-virtio-disk0 30G --hmp
root@mail:~# 

Now, ensure dmesg within the VM indicates it has been notified of the size increase:

root@mail:~# dmesg -T 
[Fri Oct 18 20:21:53 2019] vda: detected capacity change from 21474836480 to 32212254720

Whenever I make a VM, I always make swap really tiny as vda1, and the rest goes to the root partition as vda2, so increases are always possible without a full reinstall.

Now, we need to physically resize the partition within the VM. We’re going to use fdisk to first take note of the existing root partition and its starting sector, then delete it, recreate it at the same start sector, and re-apply the boot flag.

First, learn what the disk layout currently is and the current start sector for the root partition:

root@mail:~# fdisk -l 

Disk /dev/vda: 20 GiB, 21474836480 bytes, 41943040 sectors
 Units: sectors of 1 * 512 = 512 bytes
 Sector size (logical/physical): 512 bytes / 512 bytes
 I/O size (minimum/optimal): 512 bytes / 512 bytes
 Disklabel type: dos
 Disk identifier: 0x6e63ab94

Device     Boot   Start      End  Sectors Size Id Type
 /dev/vda1          2048  2099199  2097152   1G 82 Linux swap / Solaris
 /dev/vda2  *    2099200 41943039 39843840  19G 83 Linux

Now we’re ready to delete and recreate the partition

root@mail:~# fdisk /dev/vda
 Welcome to fdisk (util-linux 2.25.2).
 Changes will remain in memory only, until you decide to write them.
 Be careful before using the write command.

Command (m for help): d
 Partition number (1,2, default 2): 

Partition 2 has been deleted.

Command (m for help): n
 Partition type
    p   primary (1 primary, 0 extended, 3 free)
    e   extended (container for logical partitions)
 Select (default p): 

Using default response p.
 Partition number (2-4, default 2): 
 First sector (2099200-62914559, default 2099200): 2099200
 Last sector, +sectors or +size{K,M,G,T,P} (2099200-62914559, default 62914559): 

Created a new partition 2 of type 'Linux' and of size 29 GiB.

Command (m for help): p
 Disk /dev/vda: 30 GiB, 32212254720 bytes, 62914560 sectors
 Units: sectors of 1 * 512 = 512 bytes
 Sector size (logical/physical): 512 bytes / 512 bytes
 I/O size (minimum/optimal): 512 bytes / 512 bytes
 Disklabel type: dos
 Disk identifier: 0x6e63ab94

Device     Boot   Start      End  Sectors Size Id Type
 /dev/vda1          2048  2099199  2097152   1G 82 Linux swap / Solaris
 /dev/vda2       2099200 62914559 60815360  29G 83 Linux

Command (m for help): a
 Partition number (1,2, default 2): 

The bootable flag on partition 2 is enabled now.

Command (m for help): w
 The partition table has been altered.
 Calling ioctl() to re-read partition table.
 Re-reading the partition table failed.: Device or resource busy

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).

root@mail:~# 

That warning at the end is expected and can be safely ignored. Last step is to resize the filesystem itself. Luckily ext4 lets you do this online in a single easy command:

root@mail:~# resize2fs /dev/vda2
 resize2fs 1.42.12 (29-Aug-2014)
 Filesystem at /dev/vda2 is mounted on /; on-line resizing required
 old_desc_blocks = 2, new_desc_blocks = 2
 The filesystem on /dev/vda2 is now 7601920 (4k) blocks long.
root@mail:~#

There, much better:

root@mail:~# df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/vda2      ext4       29G   17G   11G  61% /

This method is slightly dangerous if done wrong, but ideally you keep daily backups, right? Besides, why incur needless downtime and ruin a 320+ day uptime?