I have a certain custom Raspbian image downloaded from the internet for a project which I need to use. The file is a little less than 32 GB and I am having issues getting a card which can fit that size. I know that the image definitely does not use all the space and it is probably the OS filling up the space of the SD card it was made on. I tried looking up how to shrink the size of the image but all the methods I saw involved booting the image on a pi which I cannot do since I downloaded the image online. I even got ahold of a 32 GB card to solve the issue but then discovered that the card's true capacity is 31.9 GB, and so my image burning software says I have too little space. Does anyone know how I could shrink the size of this image to 16 GB?
Assuming this is an image of an
ext-formatted partition you can simply use
resize2fs - as available on the pi itself or typical linux installations. So either you have a running linux somewhere, can put the image on an external USB-storage and access it via the pi, or can share it via network (including write premission) and use the pi. If this is given run
resize2fs -M name_of_image
The first command runs a sanity check on the image, the second one resizes it to the minimum possible (i.e. skips empty parts).
Once the image is burnt to the SD, you may want to increase the size of the system to the maximum of the SD. Again
resize will help in that case, however this is only possible when booted from the image or when the SD is directly connected to a pi/linux system. Alternatively windows tools for growing the partition should be available.
As stated in the comments
resize2fs works on images of partitions only. So you would need to create separate images for the partitions.
In the following we'll thus
a) mount the big image you downloaded
b) extract the image of the rootfs partition & resize
c) prepare a partition table for the new SD
d) copy the old boot and the new, smaller rottfs partitions & extend rootfs to the maximum
a) Mounting a multi-partition disk image
losetup will do the trick here and create
/dev entries for the disk and partitions on the image:
losetup -Pf big.img
List them with just
losetup (no options). In my example I took one of the official RasPi OS images and got it on
/dev/loop10 0 0 0 0 /data/downloads/2020-12-02-raspios-buster-armhf-lite.img 0 512
/dev will also have the two partitions ready:
/dev/loop10 /dev/loop10p1 /dev/loop10p2
lsblk for some more info:
lsblk -o NAME,FSTYPE,LABEL,SIZE /dev/loop10
NAME FSTYPE LABEL SIZE
├─loop10p1 vfat boot 256M
└─loop10p2 ext4 rootfs 1.5G
All as expected. Note that the downloaded file for the light image is ~440MB, yet the image shows 1.7GB. This is due to the partition table.
We'll make a quick trick by writing zeros to empty spaces of the partition image, this will make copying the image faster. Mount the image, create a dummy file, write zeros to it to fill up empty space, remove the dummy file and unmount the device. (Please note that this is only necessary if the image was not created with zeroed empty space). Be aware of
dd-dangers and make sure you have no typos!
mount /dev/loop10p2 /mnt
dd if=/dev/zero of=/mnt/dummy
b) Extracting the image of rootfs
dd is the best tool here, please again be aware of the dangers of
dd and triple check each
dd-command you execute.
boot is no issue, the size should be around 250MB. Yet the big image must be copied once on the hard drive, say under your home:
dd if=/dev/loop10p2 of=/home/joe/local_rootfs.img bs=4M conv=sparce
A bytesize (bs) of 4M should be reasonably fast, conv=sparce will quickly skip empty parts - including the zeros we created in the last step of the previous section.
Now shrink the image to the minimum available:
resize2fs -M /home/joe/local_rootfs.img
It will now be the size of the data only.
c) Preparing the new SD
First of all, we will just copy the master boot record (MBR). Put your SD in and check which device it is with
lsblk. Say it is
/dev/sdb. AGAIN: Make sure you get the right device and be careful with
We will copy the partition table of the image to the SD:
dd if=/path/to/big.img of=/dev/sdb bs=512 count=1
and delete the second partition in the table, while creating a new one. Example:
Device Boot Start End Sectors Size Id Type
/dev/sdb1 2048 526335 524288 256M 83 Linux
>> /dev/sdb2 526336 4095999 3569664 1.7G 83 Linux
Navigate to the second partition and delete it, then create a new one with the maximum possible size (default value for new partition), write it and confirm with "yes", then quit. No need to care about which FS-type - this will come from the rootfs partition image.
d) Copy old boot and shrunk rootfs + expand it to SD size
Boot contents may just be copied, thus
dd if=/loop12p1 of=/dev/sdb1 bs=4M
As we copied the partition table, the size will fit exactly. Analogous we copy the content of the now smaller rootfs to the new SD partition:
dd if=/home/joe/local_rootfs.img of=/dev/sdb2 bs=4M
Resize the file system of the rootfs to fill up to the maximum available on the new SD:
(Maybe you need to run a
fsck beforehand as above).
It might have happened, that the UUIDs of your new partitions have changed. Mount the new rootfs and compare
fstab with the PARTUUIDs of the partitions. Example:
mount /dev/sdb2 /mnt
proc /proc proc defaults 0 0
PARTUUID=067e19d7-01 /boot vfat defaults 0 2
PARTUUID=067e19d7-02 / ext4 defaults,noatime 0 1
versus results of
sdb2 - if need be, update the
fstab file - AND be sure to do it on the mount not your local
sync and unmount just to be sure that all got written to the SD card properly before you remove it.