Truncate empy space from a raw dd image

I have a raw disk image I took using dd of a Ubuntu instance. The total disk size is 300 GB, but only 5.5 GB used.

Is there a way to resize the raw dd image down to 20 GB, keeping all of the 5.5 GB of data, and just truncating empty blocks?


Solution 1:

Check for sector sizes:

sudo fdisk -l '/home/user/images/test.img'

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: 0x00070424

Device                      Boot Start     End Sectors  Size Id Type
/home/user/images/test.img1 *     2048   26623   24576   12M  e W95 FAT16
/home/user/images/test.img2      26624 7200767 7174144  3,4G 83 Linux

Use end number (7200767) as reference add 1 and * 512 like below:

sudo truncate --size=$[(7200767+1)*512] '/home/user/images/test.img'

Your file should be truncated

Solution 2:

Since it is a raw image, it will always be exactly the same size as the instance you made it from. However, there are a few tricks you can use to achieve the same.

Zeroing

Writing zeroes to the areas with unused space prepares it for compression that effectively reduces the size to less than the used space:

sudo mount -o loop instance.img /testmount
sudo dd if=/dev/zero of=/testmount/zero
sudo rm -f /testmount/zero
sudo umount /testmount

Compression

Simply compressing the image will make it smaller, regardless if you zeroed it first:

gzip instance.img

or, for a more modern approach:

zstd -T0 instance.img

Filesystem-aware imaging

If the image does not have to be raw (i.e. a representation of the block device rather than the content), you also have the option of using filesystem-aware tools to create the image. Zeroing is not needed in this case. For example, ZFS comes with built-in support for this, example with comression:

sudo zfs snapshot myvms/myinstance@myimage
sudo zfs send myvms/myinstance@myimage | zstd -T0 > instance.zfs.zst