Shrinking the root partition can be a challenging task since it is usually mounted and in use by the operating system. The Linux kernel does not allow you to unmount the root partition while the system is running.
One solution to this problem is to use a live USB or CD to boot into a separate environment where the root partition is not mounted and can be resized. However, the guide explores an alternative method that does not require an external device.
1. Introduction
Initramfs is an initial ramdisk that is loaded by the bootloader and used by the kernel during the boot process. It contains a minimal set of tools and drivers that are needed to mount the root filesystem and continue with the boot process.
By modifying the initrd image, we can add the necessary tools and scripts to unmount and shrink the root partition. The exact steps for modifying the initrd image may vary depending on the Linux distribution, but for Ubuntu, it typically involves unpacking the initrd image, making the necessary modifications, and repacking the image.
It's important to note that this process should only be attempted by advanced users or system administrators who have a good understanding of the underlying system and the potential risks involved. Careful planning and backup of important data is highly recommended before attempting to shrink the root partition.
2. Install kexec
kexec allows a Linux system to "reboot" without restarting the hardware. Here we will use it to reboot the current kernel with the modified initrd which will handle the shrink operation.
To install kexec, type:
sudo apt install kexec-tools
Note: Kexec will not work properly in a virtual machine.
3. Decompress initrd image
The initrd image is typically compressed to save space and reduce the time it takes to load into memory during the boot process. Each kernel has a unique initrd image that can be found in /boot directory. These images will get automatically rebuilt during important system updates like a new kernel or a dkms module.
To list initrd image, type
ls /boot

Let's decompress initrd image file to extract the files.
Make a directory named init inside /tmp and change the directory to it.
mkdir /tmp/init
cd /tmp/init
Here we will use unmkinitramfs command which is part of initramfs-tools package to decompress .img file.
sudo unmkinitramfs /boot/initrd.img-$(uname -r) .
The command will decompress the initrd image of the current running kernel. You may also use alternative tools such dracut for the same purpose.
After decompressing, there should be 3 directories in /tmp/init such as early, early2, and main.

early: Holds microcode for AMD-based processors

early2: Holds microcode for intel-based processors
main: Rootfs of the boot process. This folder itself is a small root partition.

The main folder it is pretty similar to the root partition except for some missing folders like /proc,/dev,/sys. They will get mounted or created by the init script at the beginning of the boot process.
The init file in main directory is the first process that will be started, and we will change that script to handle the shrinking task.
4. Copy essential binaries to image
Shrinking a partition needs several programs that do not exist in initrd image.
List of required programs:
- resize2fs: For changing size of an ext2/ext3/ext4 partition
- e2fsck: To check file system health, required by resize2fs before changing partition size. If the partition is corrupted, resize2fs will not execute.
- fdisk: Modify the partition table, create/delete partitions
- mkfs.ext4: Used for creating ext4 file system
Copy required binaries to the bin which resides under the main directory:
sudo cp $(which fdisk) $(which resize2fs) $(which e2fsck) $(which mkfs.ext4) ./main/bin
5. Create a Script to perform Shrink
Before creating the script, let's collect some prerequisite information.
Find the mount point of the root partition in Linux
findmnt --noheadings / | awk '{print $2}'
Find start sector of the root partition.
Use fdisk to check the start sector of root partition. This is critical because after deleting and recreating it must start with the same start sector.
sudo fdisk /dev/nbd1
Type print to see partition table information:

In my case, nbd1p2 is the root partition and 4196352 is the start sector.
In the script, we will use e2fsck to check filesystem health and resize2fs to shrink the filesystem.
Using fdisk we perform the following task in the script.
- Start delete command
- Choose partition number 2 for deletion
- Create a new partition
- Make it a primary partition
- Enter for confirmation
- Paste the start sector of the partition, double check to be sure
- Enter total storage of root. In this case, it will be shrunk from 14GB to 5GB.
- Create another partition
- Set it as primary
- Confirm
- Default start
- Use the all available space which will be 9GB in this case
- Save changes and exit
Create a file named shrink.sh in ./main/bin.
vi /tmp/init/main/usr/bin/shrink.sh
Add the following script content:
#!/bin/sh
# Check file system health
e2fsck -fy /dev/nbd1p2
# Shrink the file system to 5GB
resize2fs /dev/nbd1p2 5G
# Delete and create new partitions with fdisk
(
echo d
echo 2
echo n
echo p
echo
echo 4196352
echo +5G
echo n
echo p
echo
echo
echo
echo w
) | sudo fdisk /dev/nbd1
# Create the ext4 file system on new partition (it will be last partition number + 1)
mkfs.ext4 /dev/nbd1p3
Make the script executable, type:
sudo chmod +x /tmp/init/main/usr/bin/shrink.sh
6. Add script to init File
Now we can add the above-created shrink.sh script to /tmp/init/main/init file. This makes sure it gets executed before mounting the root partition.
sudo sed -i '/^maybe_break premount/i /usr/bin/shrink.sh' /tmp/init/main/init
In the init script there are several breakpoints for debugging. One of them is called premount, which is to debug initramfs right before it mounts the root partition. This place is where the script execution will start and it is the most appropriate location for it. Script execution may fail due to unloaded modules if it is executed too early in the boot process.
7. Regenerating the initrd image
Creating initrd is a hard and complex process. As this is going to be for one time only no compression will be used. It will be a cpio archive image, without using any extra features.
Navigate to main folder:
cd /tmp/init/main
To recreate the initrd image, type:
find . | LC_ALL=C sort | cpio --quiet -R 0:0 -o -H newc > /tmp/shrink-initrd.img
Here's a breakdown of what each part of the command does:
find .: This command starts a recursive search in the current directory, listing all the files and directories.|: The pipe symbol is used to redirect the output of thefindcommand to thesortcommand.LC_ALL=C sort: This sorts the output offindalphabetically, with theLC_ALL=Cparameter ensuring that the sorting is done in a consistent way across different locales.|: Another pipe symbol is used to redirect the output ofsortto thecpiocommand.cpio: This command creates an archive of files, in this case, the contents of the current initrd image.--quiet: This option suppresses the verbose output ofcpio.-R 0:0: This sets the ownership of the files in the archive to root.-o: This tellscpioto create a new archive.-H newc: This specifies the format of the new archive.> /tmp/shrink-initrd.img: This redirects the output ofcpioto a new file called/tmp/shrink-initrd.img.
8. Boot into modified initrd
When the following command is executed, the kexec tool loads the current kernel and initrd image directly into memory and jumps to the start of the kernel code. This allows us to bypass the normal boot process and boot directly into the kernel with the modified initrd image.
sudo kexec -l /boot/vmlinuz-$(uname -r) --initrd=/tmp/shrink-initrd.img --reuse-cmdline
Where,
sudo kexec: This runs thekexeccommand with root privileges.-l: This specifies that we want to load a current kernel and initrd image./boot/vmlinuz-$(uname -r): This specifies the location of the kernel image to be loaded, which is located in the/bootdirectory and has the same version as the currently running kernel (as determined by theuname -rcommand).--initrd=/tmp/shrink-initrd.img: This specifies the location of the initrd image to be loaded, which we created earlier and saved to the/tmpdirectory.--reuse-cmdline: This option tellskexecto reuse the same command-line parameters that were used to start the current kernel.
Use systemctl kexec to reboot the system, and see the changes!
sudo systemctl kexec
Conclusion
In this guide, we learned how to shrink root partition without using an external device.
- Created a script to perform shrink operations. Used fdisk, resize2fs, and e2fsck for partition and file system manipulation.
- Modified the initrd image.
- Used kexec to boot without actual firmware reboot.
Comments