Resizing Qemu Disk Image

Posted on Jun 2, 2024

A while ago I created a number of virtual machines using libvirt to isolate various tasks. At the time I created the virtual disks to be 20G due to storage limitations. After upgrading my hardware and over time that 20GB wasn’t enough for basic desktop environment and single task virtual machine.

When running libvirt, there are two main formats, raw and qcow2 used with qmeu.

  • qcow2 which stands for qemu copy-on-write. is the most versatile format. This format will be smaller than the following, raw format and should be used if the filesystem does not support holes, such as Windows, zlib compression and support of multiple VM snapshots. When creating a disk image of this type, a file that only holds the necessary information for a blank disk. This can range from several hundred kilobytes to a few megabytes. The file will grow as more data gets added. However, when data is removed, the space is not reclaimed.
  • raw disk image format, which is the default when running the qemu-img create command. This format has the advantage of being simple and easy to export to all other emulators. When using this format the size that you specify when creating the image will be reflected when you run ls -lh

There are other formats supported by qemu-img, such as qcow, luks, vdi, vmdk, vpc, vmdx, and parallels. Personally, I’ve never used any other format for my virtual machines with the exception of converting from VirtualBox or VMWare.

The major difference between raw and qcow2 is that the aforementioned will be faster since it doesn’t have to go through two layers of indirection before being written. However, qcow2 does support snapshots which may be important. At home, I use qcow2 for the ability to make snapshots and I have those images on NVMe drives so the performance hit won’t be noticeable for what I am doing.

Raw Image Size Reporting

This section illustrates what was mentioned about how the reported size of a raw file compared to actual disk usage.



qemu-img create debian.img 120G
Formatting 'debian2.img', fmt=raw size=128849018880
ls -l
total 120G
-rw-r--r-- 1 root         root         120G Jun  2 01:50  debian2.img
du -h debian.img 
4.0K    debian.img
qemu-img info debian.img 
image: debian.img
file format: raw
virtual size: 120 GiB (128849018880 bytes)
disk size: 4 KiB

The ls command doesn’t understand thin provisioning and will report the maximum size. The du command will report the actual space that the file is using. Using the qemu-img command will give the results from the ls and du command.

Inside the virtual machine we partition and format the virtual disk and then write some data onto the disk.



parted 
GNU Parted 3.5
Using /dev/vdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print                                                            
Error: /dev/vdb: unrecognised disk label
Model: Virtio Block Device (virtblk)                                      
Disk /dev/vdb: 129GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags: 
(parted) mklabel gpt
(parted) mkpart                                                           
Partition name?  []?                                                      
File system type?  [ext2]?                                                
Start? 1MiB                                                               
End? -1MiB                                                                
(parted) print
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 129GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End    Size   File system  Name  Flags
 1      1049kB  129GB  129GB  ext2

(parted) quit                                                             
Information: You may need to update /etc/fstab.

mkfs.ext4 /dev/vdb1
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done                            
Creating filesystem with 31456768 4k blocks and 7864320 inodes
Filesystem UUID: 80c01d19-a3e2-41be-8f7a-0a66581bb3c8
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (131072 blocks): done
Writing superblocks and filesystem accounting information: done 
mount /dev/vdb1 /mnt
dd if=/dev/urandom of=/mnt/random.txt bs=1M count=2048

Back on the host machine we can see that the disk usage has increased.



du -h debian.img 
2.2G    debian.img

Now lets delete the file we just created.



rm /mnt/random.txt
systemctl poweroff

Although the file was deleted, and space was freed on the virtual disk, the disk usage of the host machine is still the same.



du -h debian.img 
2.2G    debian.img

To reclaim disk space, we must power off the virtual machine and run the virt-sparsify command that is in the libguests-tools package



du -h debian.img 
2.5G    debian.img
virt-sparsify --in-place debian2.img 
[   2.6] Trimming /dev/sda1
[   3.0] Sparsify in-place operation completed with no errors
du -h debian2.img 
518M    debian2.img

Resizing the Disk Image

Resizing the disk is the easy part since the boundries of the partitions and filesystems do not change so you’ll have to resize those as well. When it comes to partitions and filesystems it’s generally painless.

The qemu-img resize allows you to resize the image in two ways. You can resize it to a fixed size, or relative amount. First start by making a backup of the virtual disk.



cp debian.img debian-backup.img

Resize the virtual disk to a fixed size of 250GB.



qemu-img resize debian.img 250G
Image resized.

Resize virtual disk by adding 50G to it.



qemu-img resize debian.img +50G
Image resized.

Now that the virtual disk has been resized we can now resize the partitions. First we must load the nbd kernel module so we can bind a /dev/dbd block device to a QEMU server.



modprobe nbd
qemu-nbd -c /dev/nbd0 debian.img
parted /dev/nbd0
GNU Parted 3.5
Using /dev/nbd0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print                                                                                                                    
Model: Unknown (unknown)
Disk /dev/nbd0: 183GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End    Size   File system  Name  Flags
 1      1049kB  129GB  129GB  ext4

(parted) resizepart 1                                                     
End?  [129GB]? -1MiB                                                      
(parted) print                                                            
Model: Unknown (unknown)
Disk /dev/nbd0: 183GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End    Size   File system  Name  Flags
 1      1049kB  183GB  183GB  ext4


Next we need to resize the filesystem followed by disconnecting the virtual disk.



resize2fs /dev/nbd0p1 
resize2fs 1.47.0 (5-Feb-2023)
Resizing the filesystem on /dev/nbd0p1 to 44563968 (4k) blocks.
The filesystem on /dev/nbd0p1 is now 44563968 (4k) blocks long.
qemu-nbd -d /dev/nbd0

TIP: You can also use gparted, which is not only easier, but allows you to move partitions arround and automatically resizes filesystems for you.