This post documents a few tricks I found useful when making SD card images for Raspberry Pi projects, to minimize file size and download times while making full use of the SD card space. These instructions work for Raspbian Stretch images; not sure about older or newer versions.
The first time a Raspbian system boots, it runs a program to expand the filesystem to use the full size of the SD card. So, when you are using a Raspberry Pi system you automatically get the benefit of the full (e.g., 8GB) SD card size. The problem is that when you copy the card image you copy the whole 8GB, even if only 2GB is in use. That takes up a lot of unneeded space, and lengthens download times.
So, the first task is to reduce the size of the image to a minimum. Start by copying the SD image to a file using your favorite tool. I use the dd tool on linux:
sudo dd if=/dev/sdb of=my.img
Then get yourself a copy of Andrew Oakley’s raspbian-shrink.sh script (which automates all the steps shown in his excellent web page here).
Do something like “sudo raspbian-shrink.sh -m 300 my.img”. If all is well, you’ll end up with a new file called “output.img” that has been shrunk to the minimum usable size, plus a little bit of empty space. You may need to experiment with the value of “-m” which is the amount of free space to leave. If the amount is too small, the resizing operation will fail. The default is 256 MB, but for the images I’ve tried, 300 MB seems better.
Now that the image is shrunk, you want to replicate the magic way the image is embiggened on the first boot. You need to recreate the original setup that caused the resizing to happen. These steps can occur either on the Raspberry Pi itself, or on the image file you created, either before or after shrinking.
Using the image file, here is how to mount the two partitions (boot and root):
sudo losetup --partscan --find --show imgname.img
mkdir mnt0 mnt1
sudo mount /dev/loop0p1 mnt0
sudo mount /dev/loop0p2 mnt1
To make the needed changes:
sudo cp resize2fs_once mnt1/etc/init.d/
sudo ln -s ../init.d/resize2fs_once S01resize2fs_once
Here are the contents of the
resize2fs_once file, at least for the current version Raspbian Stretch:
### BEGIN INIT INFO
# Provides: resize2fs_once
# Default-Start: 3
# Short-Description: Resize the root filesystem to fill partition
### END INIT INFO
case "$1" in
log_daemon_msg "Starting resize2fs_once"
ROOT_DEV=$(findmnt / -o source -n) &&
resize2fs $ROOT_DEV &&
update-rc.d resize2fs_once remove &&
rm /etc/init.d/resize2fs_once &&
echo "Usage: $0 start" >&2
Then comes a slightly harder part. You need to add an item to the cmdline.txt file that’s in the boot partition of the image — if you did as I did above, that means it’s
mnt0/cmdline.txt. This file is slightly different depending on whether you are working with the full-blown version of Raspbian, where it looks like this:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=49783f5b-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh splash plymouth.ignore-serial-consoles
or with the “lite” (no GUI) version, where it looks like this:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=be962ee6-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait init=/usr/lib/raspi-config/init_resize.sh
In either case, you need to edit the file so that its one line looks like the proper version above, with
init=/usr/lib/raspi-config/init_resize.sh added in the right place. NOTE: you can’t just cut and paste in one of the lines above, as the “root=PARTUID=blah” needs to match the image on the SD card, which is unique. You need to edit the line to add the necessary bits, not copy over it.
When you’re finished, clean up:
sudo umount mnt0 mnt1
sudo losetup -D
rmdir mnt0 mnt1
Now the image file is ready to write to an SD card.