Shrink disk images

Posted by Marcus Folkesson on Thursday, January 29, 2026

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.img

The root partition inside the image is only using a fraction of that space, the rest is just empty void which could be truncated away.

/media/tux_shrinking.png

(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/loop0

Associate the loop device with the disk image file, using -P to scan for partitions:

1$ sudo losetup /dev/loop0 -P disk.img

Check 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 part

Shrink 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 blocks

Resize 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 8G

Shrink 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 Linux

The partition ends on sector 18874367.

Deteach the loop device:

1$ sudo losetup -d /dev/loop0

To 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.img

Result

The disk image file is no longer 32GB but shrunk to around 9GB:

1$ du -sh ./disk.img
29,1G	disk.img

Which fits nicely on my 16GB uSD card.