When installing Raspbian, the first thing you're told to do is to expand the file system to stretch across the whole SD card. Works great. But what if I want to save space when doing a disk image. Can I reverse the expansion, and end up with a small disk image which I can use to copy my system to other SD cards and then expand it again on them?

  • 1
    ~"you're told to do". Told by whom? – IgorGanapolsky Jan 26 '17 at 14:15

[If all you want to do is shrink the partition on the card, there is a simpler way to do this than explained here by first using resize2fs on the filesystem, then using fdisk to shrink the partition; there should be many examples of this around online, e.g., at Unix & Linux. The method below involves creating a new, smaller card image. Either way, you cannot do this to a running system.]

You can't shrink a device image (although you can shrink an individual filesystem with resize2fs), but you can create one of whatever size and duplicate the contents of another one. I'll discuss this in relation to Raspbian but it is the same for any other GNU/Linux system.

A device image is for our purposes a complete byte for byte copy of a bootable device medium such an SD card, which may contain zero or more filesystem partitions. This is distinct from a filesystem image, which is a byte for byte copy of a single partition. See here for more about these important distinctions.

This answer describes how to create a device image with filesystem partitions in it, but those partitions will be empty. You then need to copy the contents of each partition in as per my other answer here. This is the most flexible method.

Some of these commands need to be run root so I recommend you just su root. You could use the pi itself to do this if you have storage big enough attached.

There are two partitions in the Raspbian image, the first small boot partition and the second larger root filesystem. There is also a partition table, aka. the master boot record (MBR), since this is a device image. The MBR is critical and occupies the first 512 bytes in the image.

A device image starts as an empty file. You need to decide how big you want it to be; du -h and df may be useful in this regard (see man du and man df; all the commands below also have man pages worth at least a glance).

I'll use a small one as an example.

dd if=/dev/zero of=test.img bs=4096 count=10240

That's ~40 MiB; the total size is determined by bs * count, the default unit is bytes. This empty file can now be treated as a device.

fdisk test.img

Fdisk is interactive and will note that the "device does not contain a recognized partition table". The first thing you have to do is create one. An MBR formatted device uses a DOS partition table, and the fdisk command to create that is o.

Command (m for help): o
Created a new DOS disklabel with disk identifier 0xe7a0dfdf.

You can see the empty table with p. We now need to create the two new partitions with n.

Command (m for help): n

Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-79999, default 2048): 8192

Note I did not use the default value; 8192 is what's used in the actual Raspbian images and seems to be a common practice with SD cards.1 In the Raspbian image this partition is ~56 MB and so ends at sector 122879. It does not have to be that big but you might as well duplicate the pattern. In this example, though, there isn't enough space, so I used 10 MB:

Last sector, +sectors or +size{K,M,G,T,P} (8192-79999, default 79999): +10M

By default the type of the partition is "Linux". We need to change that.

Command (m for help): t

Selected partition 1
Hex code (type L to list all codes): c

Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'.

Now the second partition.

Command (m for help): n

Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): 

Using default response p.
Partition number (2-4, default 2): 2
First sector (2048-79999, default 2048): 28672

Notice again I didn't use the default; because we left empty space at the beginning, fdisk still wants to use it. Instead I used the sector2 number after the last sector of the first partition, which you can find in the partition table (it's the End).

For the last sector I chose the highest possible value (79999) to fill the entire image file.

Once that's created the table looks like this:

Device    Boot     Start       End Blocks  Id System
test.img1           8192     28671  10240   c W95 FAT32 (LBA)
test.img2          28672     79999  25664  83 Linux

That's all we need here, but to hedge against mistakes, fdisk doesn't really do anything until you press w. At that point it will write out the new table and exit. To check:

> file test.img 
test.img: ; partition 1 : ID=0xc, start-CHS (0x0,130,3), end-CHS     (0x1,200,7), startsector 8192, 20480 sectors; partition 2 : ID=0x83, start-CHS (0x1,200,8), end-CHS (0x4,249,53), startsector 28672, 51328 sectors

It's recognized as a device with filesystem partitions. Those need to be formatted. To do this, we need to indicate appropriate regions of the image file as filesystems to the OS. First:

losetup -o 4194304 /dev/loop1 ./test.img
losetup -o 14680064 /dev/loop2 ./test.img

Those offset (-o) values are the Start numbers from the partition table, 8192 and 28672, times the unit size (512). Now we have device partitions that can be formatted:

> mkfs.vfat -F32 /dev/loop1
mkfs.fat 3.0.27 (2014-11-12)
Loop device does not match a floppy size, using default hd params
> mkfs.ext4 /dev/loop2

That warning about floppy size can be ignored. The second command will produce more output but there should not be any errors reported. We can now check the filesystems; first discard the loop devices with losetup -D, then create a couple of directories and mount the image partitions there:

mkdir one; mkdir two
mount -o offset=4194304 -t vfat test.img one
mount -o offset=14680064 -t ext4 test.img two

This should not report any errors (if you get "overlapping loop", see here and search the page for "overlapping loop"). one will be empty, and two should have a lost+found directory in it.

You can now copy whatever you want into these mount points, e.g., you could duplicate a pi using rsync, as per that other Q&A linked earlier. When you're done, umount the directories and you can dd test.img onto an SD card and use it.

1. If you are trying to duplicate some other image/card than a Raspbian one, you can get these numbers by looking at the image/card's partition table the same way with fdisk.

2. Generally the units used by fdisk are indicated when you view the partition table (e.g. Units: sectors of 1 * 512 = 512 bytes) but beware that it may also refer to Blocks in the table (as above) which are 1 KiB. This can be confusing because the Start and End figures aren't in "blocks", they're in units indicated at the top. Here those are 1/2 KiB, hence, the distance between those figures is twice the number of Blocks. Not all versions of fdisk do this, some report Sectors, which makes more sense.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.