{"id":156,"date":"2017-02-27T20:31:28","date_gmt":"2017-02-27T19:31:28","guid":{"rendered":"https:\/\/linuxundich.de\/en\/pishrink-shrinks-raspberry-pi-images\/"},"modified":"2017-02-27T20:31:28","modified_gmt":"2017-02-27T19:31:28","slug":"pishrink-shrinks-raspberry-pi-images","status":"publish","type":"post","link":"https:\/\/linuxundich.de\/en\/pishrink-shrinks-raspberry-pi-images\/","title":{"rendered":"PiShrink Shrinks Raspberry Pi Images"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In an earlier article, I described how to back up and restore a Raspberry Pi installation from its memory card \u2013 no shit, Sherlock. One question came up again and again, though: the image file ends up exactly as large as the memory card, even if there&#8217;s still plenty of free space on it. The &#8220;empty&#8221; area is more or less packed into the image as well. As a result, the new memory card has to be at least as large as the old one. In addition, an image intended for archiving takes up a lot of storage space. So how do you let the air out of a Raspberry Pi image?<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\">On GitHub, I recently stumbled across <a href=\"https:\/\/github.com\/Drewsif\/PiShrink\/\">the PiShrink script<\/a>, which does exactly that. The little program takes a Pi image, reduces the partition size to the necessary minimum and also adds a script to the archived system that automatically expands the partition to the maximum available space on the next boot on real hardware. This way, you can quickly shrink the image of a Raspbian system from several gigabytes to a few hundred megabytes \u2013 without much effort. You also save time when restoring, since the file system automatically expands to its full size again the first time the Raspberry Pi boots.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>mkdir ~\/bin<\/strong>\n$ <strong>wget https:\/\/raw.githubusercontent.com\/Drewsif\/PiShrink\/master\/pishrink.sh -P ~\/bin<\/strong>\n$ <strong>chmod +x ~\/bin\/pishrink.sh<\/strong><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Since PiShrink is a simple Bash script, installing it isn&#8217;t particularly complicated: you just download the script and set the execute permission. Personally, I put scripts like this in the <code>~\/bin<\/code> directory in my home folder. The system should automatically add this folder to your user&#8217;s <code>$PATH<\/code>, so you can run the command from anywhere without specifying a path. If that&#8217;s not the case for you, there are plenty of guides online on <a href=\"https:\/\/askubuntu.com\/questions\/60218\/how-to-add-a-directory-to-the-path\">extending the environment variable<\/a> (the linked article refers to Ubuntu, but the procedure is very similar on other distributions).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PiShrink compresses Raspberry Pi images<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You should now be able to call the PiShrink script directly in the terminal with a bold <code>pishrink.sh<\/code>. Without an image, the script merely shows its syntax. In the end, calling it comes down to specifying an image file and, optionally, a second file name if PiShrink should leave the original Raspberry Pi image untouched. If you only specify one file name, PiShrink edits that image directly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The only switch, <code>-s<\/code>, matters if you want to shrink the image of a Pi system that isn&#8217;t based on Raspbian \u2013 for example the Kodi distributions LibreELEC or OpenELEC. More precisely, the switch tells PiShrink not to expand the system automatically on the next boot. To do this, PiShrink normally adds a command to the <code>\/etc\/rc.local<\/code> file, which Raspbian runs automatically at boot. However, this file doesn&#8217;t exist on LibreELEC (and some other distributions).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>### Show PiShrink's help:\n$ <strong>pishrink.sh<\/strong>\nUsage: \/home\/toff\/bin\/pishrink.sh &#91;-s] imagefile.img &#91;newimagefile.img]\n### Shrink an existing Pi image:\n$ <strong>sudo pishrink.sh raspberry-pi.img<\/strong>\n### Shrink the image and keep the original:\n$ <strong>sudo pishrink.sh raspberry-pi.img raspberry-pi_pishrink.img<\/strong>\n### Disable automatic expansion of the partition:\n$ <strong>sudo pishrink.sh -s raspberry-pi.img raspberry-pi_small.img<\/strong><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So how does it all come together? As an example, I&#8217;m using a current installation of Raspbian Lite on a 16 GB SD card. You insert it into the card reader, determine the device ID with <code>lsblk<\/code> and then back up the image to the hard disk with <code>dd<\/code>. The image file then sits on the hard disk as <code>raspberry-pi.img<\/code>, around 16 GB in size \u2013 even though the image is actually mostly empty. Apart from the Raspbian system and a few configuration files, there&#8217;s no other data on the system.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>lsblk<\/strong>\nNAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT\n&#91;...]\nsdd      8:48   1  14.9G  0 disk\n\u251c\u2500sdd1   8:49   1    63M  0 part \/run\/media\/&#91;...]\/boot\n\u2514\u2500sdd2   8:50   1  14.8G  0 part \/run\/media\/&#91;...]\/0aed834e-8c8f-412d-a276-a26\n$ <strong>sudo dd if=\/dev\/sdd of=~\/raspberry-pi.img<\/strong>\n15894831104 bytes (16 GB, 15 GiB) copied, 210 s, 75.7 MB\/s\n31116288+0 records in\n31116288+0 records out\n15931539456 bytes (16 GB, 15 GiB) copied, 210.459 s, 75.7 MB\/s\n$ <strong>ls -alh ~\/raspberry-pi.img<\/strong>\n-rw-r--r-- 1 root root 15G Feb 24 21:48 raspberry-pi.img<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then you call PiShrink accordingly \u2013 since this is a Raspbian system, I leave out the <code>-s<\/code> option. PiShrink first copies the image (so make sure there&#8217;s enough free space), mounts the copy in a temporary directory under <code>\/tmp<\/code> and then reduces the size of the image: from just under 16 GB originally to only about 1.6 GB. The effect you achieve naturally depends on how much real data was stored on the Raspberry Pi&#8217;s SD card.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>sudo pishrink.sh raspberry-pi.img raspberry-pi_pishrink.img<\/strong>\nCopying raspberry-pi.img to raspberry-pi_pishrink.img...\nCreating new \/etc\/rc.local\ne2fsck 1.43.4 (31-Jan-2017)\nPass 1: Checking inodes, blocks, and sizes\nPass 2: Checking directory structure\nPass 3: Checking directory connectivity\nPass 4: Checking reference counts\nPass 5: Checking group summary information\n\/dev\/loop2: 35832\/959616 files (0.2% non-contiguous), 321790\/3872384 blocks\nresize2fs 1.43.4 (31-Jan-2017)\nresize2fs 1.43.4 (31-Jan-2017)\nResizing the filesystem on \/dev\/loop2 to 382429 (4k) blocks.\nBegin pass 2 (max = 40814)\nRelocating blocks             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nBegin pass 3 (max = 119)\nScanning inode table          XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nBegin pass 4 (max = 3178)\nUpdating inode references     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nThe filesystem on \/dev\/loop2 is now 382429 (4k) blocks long.\n\nShrunk raspberry-pi_pishrink.img from 15G to 1.6G\n$ <strong>ls -alh raspberry-pi*<\/strong>\n-rw-r--r-- 1 root root  15G Feb 27 11:08 raspberry-pi.img\n-rw-r--r-- 1 root root 1.6G Feb 27 11:30 raspberry-pi_pishrink.img\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Restoring a shrunk Pi image<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As a test, the Raspbian system originally installed on a 16 GB SD card is to move to a microSD card with only 8 GB. To do this, I write the image shrunk with PiShrink back to the new card with <code>dd<\/code>, insert it into the Raspberry Pi and boot the shrunk system. To make full use of the available storage space, the system automatically reboots once during the first boot. After that, you land in the familiar Raspbian system \u2013 a <code>df -h<\/code> shows that the system now uses the entire SD card.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>lsblk<\/strong>\nNAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT\n&#91;...]\nsdd      8:48   1   7.4G  0 disk\n\u251c\u2500sdd1   8:49   1    63M  0 part \/run\/media\/&#91;...]\/boot\n\u2514\u2500sdd2   8:50   1   7.3G  0 part \/run\/media\/&#91;...]\/0aed834e-8c8f-412d-a276-a26\n$ <strong>sudo dd if=raspberry-pi_pishrink.img of=\/dev\/sdd bs=1M; sync<\/strong>\n1560+1 records in\n1560+1 records out\n1636684288 bytes (1.6 GB, 1.5 GiB) copied, 116.203 s, 14.1 MB\/s<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>pi@raspberrypi:~ $ <strong>df -h<\/strong>\nFilesystem      Size  Used Avail Use% Mounted on\n\/dev\/root       7.2G  990M  6.0G  14% \/\ndevtmpfs        459M     0  459M   0% \/dev\ntmpfs           463M     0  463M   0% \/dev\/shm\ntmpfs           463M  6.2M  457M   2% \/run\ntmpfs           5.0M  4.0K  5.0M   1% \/run\/lock\ntmpfs           463M     0  463M   0% \/sys\/fs\/cgroup\n\/dev\/mmcblk0p1   63M   21M   42M  33% \/boot<\/code><\/pre>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ac59aa044d44&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ac59aa044d44\" class=\"wp-block-image size-full wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/linuxundich.de\/wp-content\/uploads\/2017\/02\/pishrink-beispiel.png\" alt=\"PiShrink running in the terminal; the file manager next to it shows the original at 15.9 GB and the shrunk copy at 811 MB.\" style=\"width:100%\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">PiShrink reduces the image of a Raspberry Pi system to its minimum size.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you back up a Raspberry Pi system that isn&#8217;t based on Raspbian and has no <code>\/etc\/rc.local<\/code> \u2013 such as the Kodi distributions LibreELEC or OpenELEC mentioned above \u2013 error messages appear when shrinking it with PiShrink. The reason: by default, PiShrink tries to add a command to this file that later expands the partition automatically. To avoid this, you have to use the <code>-s<\/code> option. That way, you can shrink the image successfully even without an <code>rc.local<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>### Error messages when shrinking a LibreELEC image:\n$ <strong>sudo pishrink.sh libreelec.img<\/strong>\n&#91;...]\nmd5sum: \/tmp\/tmp.4ornQ3Va4y\/etc\/rc.local: No such file or directory\n\/home\/&#91;...]\/bin\/pishrink.sh: line 63: &#91;: !=: unary operator expected\n&#91;...]\n### LibreELEC, OpenELEC and co. are better shrunk like this:\n$ <strong>sudo pishrink.sh -s libreelec.img<\/strong>\nSkipping autoexpanding process...\ne2fsck 1.43.4 (31-Jan-2017)\n\/dev\/loop2: recovering journal\nPass 1: Checking inodes, blocks, and sizes\nPass 2: Checking directory structure\nPass 3: Checking directory connectivity\nPass 4: Checking reference counts\nPass 5: Checking group summary information\n\/dev\/loop2: 861\/1810432 files (1.0% non-contiguous), 252448\/7235584 blocks\nresize2fs 1.43.4 (31-Jan-2017)\nresize2fs 1.43.4 (31-Jan-2017)\nResizing the filesystem on \/dev\/loop2 to 49976 (1k) blocks.\nBegin pass 2 (max = 15759)\nRelocating blocks             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nBegin pass 3 (max = 884)\nScanning inode table          XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nBegin pass 4 (max = 143)\nUpdating inode references     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nThe filesystem on \/dev\/loop2 is now 49976 (1k) blocks long.\n\nShrunk libreelec.img from 7.5G to 563M<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">However, the restored system then doesn&#8217;t expand automatically, because LibreELEC (and, as far as I know, OpenELEC too) offers no option in its menus to manually expand the data partition to the full card size. The internal resizing routine only kicks in on a freshly copied first installation, as you can see in the <a href=\"https:\/\/github.com\/LibreELEC\/LibreELEC.tv\/blob\/master\/packages\/sysutils\/busybox\/scripts\/fs-resize\">script<\/a>. To make full use of the partition anyway, the only option is to fix it up manually with a partitioning tool such as GParted. On Linux, GParted is readily available; Windows users can use the tool via a <a href=\"https:\/\/gparted.org\/liveusb.php\">live CD or live USB stick<\/a>.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ac59aa0458dc&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ac59aa0458dc\" class=\"wp-block-image size-full wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/linuxundich.de\/wp-content\/uploads\/2017\/02\/gparted-partition-vergroessern.png\" alt=\"GParted shows a LibreELEC card with 6.85 GB of unallocated space and the dialog for resizing the partition.\" style=\"width:100%\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">With tools such as GParted, you can quickly adjust the partition size again.<\/figcaption><\/figure>\n\n","protected":false},"excerpt":{"rendered":"<p>PiShrink shrinks the image of a Raspberry Pi SD card to the space actually used and automatically expands the system again on first boot.<\/p>\n","protected":false},"author":2,"featured_media":160,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"lui_source_id":40282,"lui_source_hash":"e214df9031545d421d83888652653b7c0e701f37688581adf63153f2ebe85b6f","lui_source_translated":"2026-10-06","lui_source_reviewed":true,"lui_via_url":"","lui_via_label":"","lui_source_url":"","lui_source_label":"","footnotes":""},"categories":[6],"tags":[],"class_list":["post-156","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts\/156","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/comments?post=156"}],"version-history":[{"count":0,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts\/156\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/media\/160"}],"wp:attachment":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/media?parent=156"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/categories?post=156"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/tags?post=156"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}