A couple days ago I received my second Raspberry Pi. Since I had already tinkered with the configuration and installation of additional software packages I wanted to clone what I have to a new SD card and use this for my second Pi.

The only problem: The SD card I wanted to use for the second Pi is 16GB. The SD card in use by the first Pi is 32GB. In general this should not be a problem since I barely use 5GB from the available 32GB. Anyway, the question was: How do I get the system on the other Pi?

One way (which I did not try) could have been to simply create the necessary partitions on the new card, copy the contents from the 32GB card one partition at a time to the other SD card and enjoy the new system.

Frankly, I feel more comfortable with having one file that I just burn to the destination card without caring about partitions, files and whatever else.

So, how do I get an image of a SD card that I can then use to create a clone? The next tool at hand to go with is dd. But dd is meant to do raw a copy, sector by sector, bit by bit! This would even be easier than before since I would just have to push everything from the source SD card into a file and then push this image file to a destination SD card. The only condition with this approach is: The destination card must be of the same or greater size than the source card! But in my the destination card is smaller than the source.

To work around this I could have used gparted to first shrink the partitions of the 32G SD card, run dd and then cut from the resulting image what I do not need. Again, I do not feel comfortable with altering the source just for the purpose of cloning or creating an economical copy. But, there are some steps in this example that are useful for the final solution.

The solution!

The solution that I finally put into practice is the following:

  • use dd to clone the source SD card into a file
  • use resize2fs to shrink the filesystem as much as possible
  • use fdisk to resize (delete and create) the system partition
  • use truncate to cut of trailing waste from the image file and effectively making it smaller

Setup

In my case I have a SD card reader that is merely a USB Stick for SD and micro SD cards. So, I took the SD card from my first Pi, put it in the USB stick and plugged it into the system. To find out which device I have to use for dd I use lsblk. This is what it looked like before I plugged in the USB stick:

 NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda             8:0    0   128G  0 disk 
├─sda1          8:1    0   250M  0 part /boot
├─sda2          8:2    0     1K  0 part 
└─sda5          8:5    0 127.8G  0 part 
  ├─root-swap 254:0    0     4G  0 lvm  [SWAP]
  └─root-root 254:1    0 123.8G  0 lvm  /
sdb             8:16   0    80G  0 disk 
├─sdb1          8:17   0     1K  0 part 
└─sdb5          8:21   0    80G  0 part 
sr0            11:0    1  1024M  0 rom  

And here is what it looks like after I plugged in the USB stick:

NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda             8:0    0   128G  0 disk 
├─sda1          8:1    0   250M  0 part /boot
├─sda2          8:2    0     1K  0 part 
└─sda5          8:5    0 127.8G  0 part 
  ├─root-swap 254:0    0     4G  0 lvm  [SWAP]
  └─root-root 254:1    0 123.8G  0 lvm  /
sdb             8:16   0    80G  0 disk 
├─sdb1          8:17   0     1K  0 part 
└─sdb5          8:21   0    80G  0 part 
sdc             8:32   1  29.7G  0 disk 
├─sdc1          8:33   1    63M  0 part /media/marcel/boot
└─sdc2          8:34   1  29.7G  0 part /media/marcel/2f840c69-cecb-4b10-87e4-01b9d28c231c
sr0            11:0    1  1024M  0 rom  

From this you can see, that there is a new device sdc with two partitions mounted at /media/marcel.

Clone a SD card with dd

Cloning the SD card is fairly easy with dd. But before I clone the SD card I will unmount the two partitions sdc1 and sdc1 to make sure nothing is blocked. After all, I do not need them mounted anyway!

sudo umount /media/marcel/boot
sudo umount /media/marcel/2f840c69-cecb-4b10-87e4-01b9d28c231c

Now, I can clone the SD card with:

sudo dd status=progress if=/dev/sdc of=~/pi_backup.img

After some time I get a file ~/pi_backup.img with a size of about 30G. Here is what it looks like in the console when this command finished on my system:

62333952+0 records in
62333952+0 records out
31914983424 bytes (32 GB, 30 GiB) copied, 1172.61 s, 27.2 MB/s

To verify all went well I compare the source with the destination:

sudo fdisk -l /dev/sdc

This shows me:

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

Device     Boot  Start      End  Sectors  Size Id Type
/dev/sdc1         8192   137215   129024   63M  c W95 FAT32 (LBA)
/dev/sdc2       137216 62333951 62196736 29.7G 83 Linux

Next I run:

file -s ~/pi_backup.img

Which shows me:

/home/marcel/pi_backup.img: DOS/MBR boot sector; partition 1 : ID=0xc, start-CHS (0x0,130,3), end-CHS (0x8,138,2), startsector 8192, 129024 sectors; partition 2 : ID=0x83, start-CHS (0x60,0,1), end-CHS (0x8f,3,16), startsector 137216, 62196736 sectors

From this I am able to see that on the physical source I have two partitions. One starting at sector 8192, ending at sector 129024, being 129024 sectors in size and of type FAT32. A second partition starting at sector 137216, ending at sector 62333951, being 62196736 in size and of type Linux.

Comparing this to the output from file -s ~/pi_backup.img I can confirm, that start, end, size and type are all the same. Perfect, this clone is identical!

To be on the safe side I unmounted and removed the USB stick with the source SD card.

To visialize what we have so far, here is a screen shot of what it looks like in GParted.

alt text

Shrinking the filesystem

The next step is to shrink the filesystem. For this I first mount the image file with kpartx like this:

kpartx -a ~/pi_backup.img

This will mount the image file to a loop device and add partition mappings, as well. Running lsblk shows:

NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda             8:0    0   128G  0 disk 
├─sda1          8:1    0   250M  0 part /boot
├─sda2          8:2    0     1K  0 part 
└─sda5          8:5    0 127.8G  0 part 
├─root-swap 254:0    0     4G  0 lvm  [SWAP]
└─root-root 254:1    0 123.8G  0 lvm  /
sdb             8:16   0    80G  0 disk 
├─sdb1          8:17   0     1K  0 part 
└─sdb5          8:21   0    80G  0 part 
sr0            11:0    1  1024M  0 rom  
loop0           7:0    0  29.7G  0 loop 
├─loop0p1     254:2    0    63M  0 part 
└─loop0p2     254:3    0  29.7G  0 part 

From this I one can see that the image file pi_backup.img is now mounted to loop0 with the first partition mapped to loop0p1 and the second partition to loop0p2.

To shrink the filesystem on the second partition I first have to check the filesystem with:

sudo e2fsck -f /dev/mapper/loop0p2

After that, I can now shrink the filesystem with:

sudo resize2fs -M /dev/mapper/loop0p2 

After resize2fs is done it tells me:

Resizing the filesystem on /dev/mapper/loop0p2 to 1555124 (4k) blocks.
The filesystem on /dev/mapper/loop0p2 is now 1555124 (4k) blocks long.

That is, the filesystem is now 1555124 * 4k in size, that is 6220496k which in turn is about 6074MB or 5.93G.

Shrink the partition

Nex I want to shrink the partition to the size of the shrinked filesystem. For this, I first remove the mapping from kpartx with:

sudo kpartx -d /dev/loop0

This removes the two partition mappings loop0p1 and loop0p2, but leaves the image available as loop0. Now I am able to use the power of fdisk:

sudo fdisk /dev/loop0

Typing p shows me the current available partitions on loop0:

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

Device       Boot  Start      End  Sectors  Size Id Type
/dev/loop0p1        8192   137215   129024   63M  c W95 FAT32 (LBA)
/dev/loop0p2      137216 62333951 62196736 29.7G 83 Linux

Now, the partition on /dev/loop0p2 is still 29.7G big. To shrink it, I’ll have to delete it and create a new partition. So, I type d to delete the second partition (default offered). From the list I got with p I know where the first partition ended and the second started. With this in mind I am able to create a new partition with a smaller size. For this, I type n followd by p and accept the default for the partition number (default 2).

The first sector should be the same as before, that is 137216. The last sector is an estimate. From the math I did after shrinking I know that I need about 6G. To be on the save side, I add 1G more. Just to be sure! So, for the last sector wanted by fdisk I enter +7G.

Now, p shows me:

Device       Boot  Start      End  Sectors Size Id Type
/dev/loop0p1        8192   137215   129024  63M  c W95 FAT32 (LBA)
/dev/loop0p2      137216 14817279 14680064   7G 83 Linux

I am happy with this, so I type w and hit ENTER. Done!

The final step is to remove the loop device:

losetup -d /dev/loop0

Checkpoint

At this point everything should look like before we started. If losetup did not work, try dmsetup remove /dev/loop0 and if that does not work, try the ultimate weapon: dmsetup remove_all. The last one should remove all loop devices that you had.

To check that everything worked as expected, I will use kpartx -a ~/pi_backup.img one more time and show the result with lsblk:

NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda             8:0    0   128G  0 disk 
├─sda1          8:1    0   250M  0 part /boot
├─sda2          8:2    0     1K  0 part 
└─sda5          8:5    0 127.8G  0 part 
  ├─root-swap 254:0    0     4G  0 lvm  [SWAP]
  └─root-root 254:1    0 123.8G  0 lvm  /
sdb             8:16   0    80G  0 disk 
├─sdb1          8:17   0     1K  0 part 
└─sdb5          8:21   0    80G  0 part 
sr0            11:0    1  1024M  0 rom  
loop0           7:0    0  29.7G  0 loop 
├─loop0p1     254:2    0    63M  0 part 
└─loop0p2     254:3    0     7G  0 part 

Again, to visualize the current state I present another screen shot.

alt text

Great! The second partition is now 7G, instead of 29.7G. So, let’s remove the mapping again and finally shrink the image file to get rid of the unused space!

sudo kpartx -d /dev/loop0
sudo losetup -d /dev/loop0

Shrinking the image file

Knowing the Image is about 30G in size with one partition of 63M and another of 7G, I estimate that keeping 8G in total should be save. So, I cut off 22G:

truncate -s -22G ~/pi_backup.img    

Let’s check again what the final image file now looks like in GParted.

alt text

Move the image to a new SD card

The final step is like the first step, just the other way around. That is, I plug in my USB stick with the new 16G SD card and call:

sudo dd status=progress if=~/pi_backup.img of=/dev/sdc

After dd finished, I have a cloned SD card that I can now insert into my second Pi. The rest is like one would do with any of the initial Pi images: After a first boot expand the filesystem with raspi-config to use all the space available on the SD card.