I recently had a Raspberry Pi image that I wanted to deploy to several Pis (is that the right plural?) for production use. During development of this, I worked on a 32GB SD card for some breathing room, but to save costs, we decided to deploy onto 4GB cards.
This left me with a small issue. Although the file system on the SD card was only using 2.5GB of space, any image files from it were still 32GB, far too large to go onto the SD cards. I need to remove the white space from the end of the image to make it fit. So, here’s a quick tutorial on shrinking raw disk images.
N.B. These screenshots were taken from a disk that got corrupted half-way through the resize (I did the maths wrong, and shaved off some of the partition), so they may look a bit different.
We can use Linux’s loop module to treat files as disk objects. First we need to load this into the kernel by running modprobe loop . Once the module is loaded, we can create a new device from the image by running losetup /dev/loop0 raspbian.img
This is where we run into our first issue. gParted can access the disk, but not any of the partitions on it. This is indicated by the warning triangles next to the partitions. If you look in /dev, you’ll find that the loop device (/dev/loop0) exists, but none of it’s partitions. The trick is to use kpartx to create the partition mappings, and then symlinks to put them in the right place. The commands should look something like this:
kpartx -a raspbian.img ln -s /dev/mapper/loop0p1 /dev/loop0p1 ln -s /dev/mapper/loop0p2 /dev/loop0p2
Now if we look in gParted, the exclamation marks are gone and we can do the resize. There’s no need to resize the underlying file system, gParted will handle all of this for you. Just drag the partition to the new size (don’t forget the /boot partition) and click the tick icon. gParted will then resize the file system and underlying partition, though it can take a while depending on the size of the partition and speed of the disk.
Now we need to trim the fat off the disk image itself. There’s no data on it, but the image file is still 32GB (or whatever size your SD card was). To do this, right-click on the unallocated space at the end of the disk image, and click on New. From here, make a note of the “Maximum size” reported by gParted. This is the total amount that we can remove from the image.
Before we can do the actual removal, it’s wise to unmount the disk image so that nothing tries to modify it while we’re doing the removal. To do that, we need to undo the modifications made by kparted, remove the symlinks, and un-mount the loop image. That’ll look something like this:
dmsetup remove /dev/mapper/loop1p* rm /dev/loop0p1 rm /dev/loop0p2 losetup -d /dev/loop0
Then we can take off the empty space using the truncate command. This takes the name of the image and the size to remove, specified using a negative number. This example will remove 26GB from the end of the file
truncate raspbian.img --size -26G
Now we have a nice small image file, ready to be written to our smaller SD cards with the software of your choice. I like dd combined with pv for a progress bar.
pv raspbian.img | dd BS=32M OF=/dev/sdcard1
Enjoy your pie!