How to Shrink Root Partition without Live CD/USB

Last updated: April 11, 2023 | Arda Atci

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
list content of /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.

contents of initrd.img

early: Holds microcode for AMD-based processors

init early directory

early2: Holds microcode for intel-based processors

main: Rootfs of the boot process. This folder itself is a small root partition.

init main directory

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:

print partition table

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 the find command to the sort command.
  • LC_ALL=C sort: This sorts the output of find alphabetically, with the LC_ALL=C parameter ensuring that the sorting is done in a consistent way across different locales.
  • |: Another pipe symbol is used to redirect the output of sort to the cpio command.
  • 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 of cpio.
  • -R 0:0: This sets the ownership of the files in the archive to root.
  • -o: This tells cpio to create a new archive.
  • -H newc: This specifies the format of the new archive.
  • > /tmp/shrink-initrd.img: This redirects the output of cpio to 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 the kexec command 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 /boot directory and has the same version as the currently running kernel (as determined by the uname -r command).
  • --initrd=/tmp/shrink-initrd.img: This specifies the location of the initrd image to be loaded, which we created earlier and saved to the /tmp directory.
  • --reuse-cmdline: This option tells kexec to 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.
SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment