Shrink disk images
Brief
I wanted to shrink a raw disk image file I had that contained multiple partitions. Nothing unusual at all. I thought it should be multiple tools out there to do this (and I'm sure there are), but the first couple of Google results were just showing some old GUI tools - which I refuse to use.
Instead of searching further, I decided to "just do it". The concept is easy, shrink the partitions inside the image file, then truncate the image file itself. The standard tools should be able to do this.
What I simply want is this image to fit on a 16GB uSD card:
1$ du -sh ./disk.img
230G disk.imgThe root partition inside the image is only using a fraction of that space, the rest is just empty void which could be truncated away.
(Heh, this image is generated with Python, see tux_shrinking.py )
Step by step
Assume we have a disk image file called disk.img containing two partitions, a small boot partition and a larger root partition.
Loopback mount the disk image
Find the first available loop device:
1$ losetup -f
2/dev/loop0Associate the loop device with the disk image file, using -P to scan for partitions:
1$ sudo losetup /dev/loop0 -P disk.imgCheck the partitions:
1$ lsblk /dev/loop0
2NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
3loop0 7:0 0 29,7G 0 loop
4├─loop0p1 259:5 0 256M 0 part
5└─loop0p2 259:6 0 29,5G 0 partShrink the filesystem
First, run e2fsck on the root partition to check for errors:
1$ sudo e2fsck -f /dev/loop0p2
2e2fsck 1.47.3 (8-Jul-2025)
3Pass 1: Checking inodes, blocks, and sizes
4Inodes that were part of a corrupted orphan linked list found. Fix<y>? yes
5Inode 10054 was part of the orphaned inode list. FIXED.
6Inode 12444 was part of the orphaned inode list. FIXED.
7Inode 12445 was part of the orphaned inode list. FIXED.
8Inode 131894 was part of the orphaned inode list. FIXED.
9Inode 516361 was part of the orphaned inode list. FIXED.
10Inode 516374 was part of the orphaned inode list. FIXED.
11Pass 2: Checking directory structure
12Pass 3: Checking directory connectivity
13Pass 4: Checking reference counts
14Pass 5: Checking group summary information
15
16rootfs: ***** FILE SYSTEM WAS MODIFIED *****
17rootfs: 125809/1903104 files (0.3% non-contiguous), 1329924/7725184 blocksResize the filesystem using resize2fs. You can specify the new size in various ways, here I set a fixed size of 8GB
1$ sudo resize2fs /dev/loop0p2 8GShrink the partition
Shrink the partition using parted. Set the unit to GiB and then resize the partition 2 (root partition) to 9GB:
1$ sudo parted /dev/loop0
2GNU Parted 3.6
3Using /dev/loop0
4Welcome to GNU Parted! Type 'help' to view a list of commands.
5(parted) unit GiB
6(parted) print
7Model: Loopback device (loopback)
8Disk /dev/loop0: 29,7GiB
9Sector size (logical/physical): 512B/512B
10Partition Table: msdos
11Disk Flags:
12
13Number Start End Size Type File system Flags
14 1 0,00GiB 0,25GiB 0,25GiB primary fat32 lba
15 2 0,25GiB 29,7GiB 29,5GiB primary ext4
16
17(parted) resizepart 2 9
18Warning: Shrinking a partition can cause data loss, are you sure you want to continue?
19Yes/No? Yes
20(parted) quit Shrink the image file itself
First, determine where the second partition ends:
1$ sudo fdisk -l /dev/loop0
2Disk /dev/loop0: 29,72 GiB, 31914983424 bytes, 62333952 sectors
3Units: sectors of 1 * 512 = 512 bytes
4Sector size (logical/physical): 512 bytes / 512 bytes
5I/O size (minimum/optimal): 512 bytes / 512 bytes
6Disklabel type: dos
7Disk identifier: 0xe62e25cc
8
9Device Boot Start End Sectors Size Id Type
10/dev/loop0p1 8192 532479 524288 256M c W95 FAT32 (LBA)
11/dev/loop0p2 532480 18874367 18341888 8,7G 83 LinuxThe partition ends on sector 18874367.
Deteach the loop device:
1$ sudo losetup -d /dev/loop0To calculate the size of the image file, multiply this number by the sector size (512 bytes) and truncate the rest:
1$ truncate --size=$[(18874367+1)*512] disk.imgResult
The disk image file is no longer 32GB but shrunk to around 9GB:
1$ du -sh ./disk.img
29,1G disk.imgWhich fits nicely on my 16GB uSD card.