Shrink KVM disk size?

Hi,

I’m currently playing around with KVM and libvirt for setting up virtual machines on a local sandbox server, and a second public server. I can install and run Rocky Linux 8 VMs just fine, and everything works perfectly.

Let’s say I want to increase the size of my VM (I’m using ext4 instead of the default XFS and my root partition is /dev/vda2). It’s relatively simple:

  1. Stop the VM.
  2. Install cloud-utils-growpart.
  3. Resize the VM using qemu-img resize rocky-image.qcow2 +5G.
  4. Run growpart -v /dev/vda 2.
  5. Run resize2fs /dev/vda2.

This works perfectly. And now the question: is there a safe way to perform this operation the other way around, e. g. shrink a VM disk when there’s much unused space ?

Cheers from the blazing hot South of France,

Niki

qemu-img convert ... is probably what ur looking for ;

Image conversion is also useful to get smaller image when using a growable format such as “qcow”: the empty sectors are detected and suppressed from the destination image.

1 Like

… shrink fs, resize partition, convert img. ( I imagine… though I’d back it up first )

… though, if you didn’t create the img with preallocation it should only be as big as was needed for the data used and won’t have space to recover.

I went with no preallocation in the end because OS installation just seemed to ignore the preallocated space and add more (even allowing for swap)

Likely some of the preallocated space is allocated to swap first and then the install extends the img for files etc leading to a bigger img, then you end up with all that “unused” swap space in the img. Whereas if you run the VM without that extra space it won’t extend the img unless you actually write to swap.

Thanks, but could you give a more detailed example please ?

I have a script for this called shrink_vm_image.sh:

#!/bin/bash
#
# Script to shrink VM qcow2

OLDIMAGE=$1
NEWIMAGE=$2

if [ -z "$OLDIMAGE" ] || [ -z "$NEWIMAGE" ]
then
  echo "You need to provide an image name to shrink from and to!"
  echo "Eg: ./shrink_vm_image.sh myvm.qcow2 mynewvm.qcow2"
else
  echo "Shrinking: $OLDIMAGE to $NEWIMAGE"
  qemu-img convert -O qcow2 -c $OLDIMAGE $NEWIMAGE
fi

the qemu-utils package will need to be installed to use it, as this package provides qemu-img. The partition size inside the VM will remain the same, but it makes the qcow file smaller and more compressed. Basically, if I create a VM with a 40GB disk, I get a 40GB qcow file. But if I use the -c parameter link in my script, it will compress the file to the used space, so let’s assume my base install has 7GB of used, space, then the qcow will more or less be 7GB instead of 40GB.

Shrinking partitions inside the VM, or even on a normal server requires to be ran offline, but it’s not safe to do, because you must shrink the partition as well as the filesystem. If you shrink the partition more than the filesystem was shrank, then you will trash the system and it will not boot.

Also not all filesystems will shrink, eg, ext4 can shrink, xfs cannot. Better start with a disk of 10GB, and then grow from there, rather than think about shrinking the filesystem/partition.

1 Like

Alas, no but I see @iwalker has come to the rescue.

I haven’t used qemu-img convert … but I notice there is nother option qemu-img resize <img> --shrink <decrease eg -10G> … but again, haven’t used it and not entirely clear how it works (see below)


Avoiding unnecessary vd space on host initially

qemu-img create -f qcow2 myVitualDisk 1000204886016 # 1TB vd, no preallocation - a few MB on host

If you install OS to that it should only be as big as necessary when ur done - there shouldn’t be any space to recover. Whereas

qemu-img create −−preallocation=full -f qcow2 myVitualDisk 1000204886016

Would use up 1TB on the host media and have space to recover.

The efficient way to do an install, have a 1TB vd and not waste host space would seem to be
qemu-img create -f qcow2 -o preallocation=full myVitualDisk 10G
qemu-img resize myVitualDisk 1000204886016
Perform install

… so the backing space etc is in place (1TB vd, 10G used on host).

However, the install will grow it beyond 10G (prob because it sees several G of that as used for swap).

So in reality its best to just not allocate anything at the start and you end up with a smaller img:
qemu-img create -f qcow2 myVitualDisk 1000204886016 and do install as normal.


shrinking

qemu-img resize <img> --shrink <decrease eg -10G or new smaller size>

Before using this command to shrink a disk image, you MUST use file system and partitioning tools inside the VM to reduce allocated file systems and partition sizes accordingly. Failure to do so will result in data loss!

When shrinking images, the “−−shrink” option must be given. This informs qemu-img that the user acknowledges all loss of data beyond the truncated image’s end.

I suspect this will shrink the img in place on the host if the new size is smaller than the initial on-host size but also shrink the vd size. … dunno how original preallocation would play into it.

so I would try

  1. Backup my vd (img)
  2. Find out how much space my vd file systems are using
  3. Shrink my file systems (depends on fs / eg. not possible with xfs)
  4. Shrink my partition(s) - again depends on setup/tools
  5. Shutdown my vm
  6. qemu-img resize <img> --shrink <decrease eg -10G or new smaller size>
  7. qemu-img resize <img> <desired vd size>

see man qemu-img

Alternatively you can work on the vd in the host by mounting as a loop device for some img types or as an nbd for qcow2:


modprobe nbd
qemu-nbd -c /dev/nbd0 -f qcow2 <img>

then cryptsetup open if its luks and mount the volume.
after unmounting and cryptsetup closing;

qemu-nbd -d /dev/nbd0

Though your mileage may vary.


EDIT (re mounting vd in host)

Just remembered !

If your vd contains vgs (volume groups) with the same name as any of those active on the host, you will not be able to activate them. (and this is likely as you will probably have used the default values to install an OS on your host and your guest where they are the same OS).

There is a way around this - you need to rename the vgs on the vd and then name them back again when ur done. This involves vgcfgbackup to get a cfg file , then changing the name in that file and then applying it with vgcfgrestore … but I seem to have lost the details and there is the question of WHICH rl-root is which… I’ll post again when I get round to redoing/documenting it - the exact syntax for vgcfgrestore is not entirely obvious and a little scary as it could be construed as amending the wrong vg.

See here for instructions.

1 Like

Yeah, the nice little disclaimer when using that shrink parameter :slight_smile:

I have shrunk partitions in the past, namely ext3 and ext4, since xfs won’t shrink, it can only be grown. But the main issue is using the filesystem tools resize2fs and specifying the new size, and then resize the partition to the file system size + 3-5% so that you don’t overwrite the end of the partition and lose data.

Attempting to do this when you are using standard partitions instead of LVM, means gaps between for example /dev/sda2 and /dev/sda3 - you cannot move the start of a partition, it will cause data loss. Therefore, if you can copy the data from /dev/sda3 and then delete the partition, recreate it after to get rid of the gap, and then format and copy the data back, then it’s doable. LVM far easier of course. LVM does have commands to grow the filesystem and LVM partition at the same time. Never used it personally, since I normally expand the LVM, and then use resize2fs for ext3/4. Not sure if the same LVM commands will also shrink both at the same time, but they would have to be unmounted to do this so booted from a rescue disk. Growing can be done online, shrinking cannot.

True, but if u can’t mount it in the host you can use a separate VM system vd and access it offline in that vm - you could even use a parted magic iso or something. I usually let KDE Partition Manager or GParted worry about the details if I can and I mainly use LVM so lots of issues avoided.