Virtualization

Resize Root Partition Without LVM on Linux: ext4 and XFS

You resize a root partition without LVM in three separate steps, and skipping any one of them leaves you staring at a full / while lsblk insists the disk got bigger. The virtual disk grows at the hypervisor. The partition grows with growpart. The filesystem grows with resize2fs on ext4 or xfs_growfs on XFS. All of it happens online, on a mounted root, with no reboot and no rescue ISO.

Original content from computingforgeeks.com - post 16136

This walks the full chain on a plain GPT layout with no volume manager in the way, including the failure messages that stop people at step two. Every command run inside the guests below was executed in September 2026 on Ubuntu 26.04.1 LTS with an ext4 root and Rocky Linux 10.2 with an XFS root.

Check what you are actually resizing

Find the device behind / and its filesystem type before touching anything. The filesystem type decides which resize command you run, and it is not always what the distro name suggests.

findmnt -no SOURCE,FSTYPE /
lsblk
df -hT /

On the Ubuntu host the root filesystem is ext4 on /dev/vda1, and the disk has already been grown to 30G at the hypervisor while the partition still sits at its original size:

/dev/vda1 ext4

NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda     253:0    0   30G  0 disk
├─vda1  253:1    0 18.9G  0 part /
├─vda13 253:13   0 1023M  0 part /boot
├─vda14 253:14   0    4M  0 part
└─vda15 253:15   0  106M  0 part /boot/efi

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/vda1      ext4   19G  3.0G   16G  16% /

Note the partition numbering. Root is partition 1 but it is physically the last partition on the disk, sitting after 13, 14 and 15. That ordering is what matters, not the number. The Rocky host is more conventional, with XFS on the fourth and final partition:

/dev/sda4 xfs

NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   20G  0 disk
├─sda1   8:1    0    2M  0 part
├─sda2   8:2    0  200M  0 part /boot/efi
├─sda3   8:3    0 1000M  0 part /boot
└─sda4   8:4    0  8.8G  0 part /

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda4      xfs   8.4G  1.7G  6.8G  20% /

Two facts to carry forward: the device name (/dev/vda1 or /dev/sda4) and the filesystem type. If lsblk shows an lvm line under the partition, stop here. That machine has a volume manager and the procedure is different, covered in the LVM root filesystem guide instead.

Extend the virtual disk first

Nothing inside the guest can create space that the block device does not have. Grow the disk on the platform that owns it. On Proxmox VE, from the node shell:

qm resize 102 virtio0 +10G

On plain libvirt or KVM, find the target name and grow the device while the guest keeps running. qemu-img resize is the offline path only, because QEMU holds the write lock on the image for as long as the guest is up:

sudo virsh domblklist web01
sudo virsh blockresize web01 vda 30G

On AWS you modify the EBS volume rather than the instance, which the EBS boot disk walkthrough covers end to end, and the offline qcow2 route lives in the qemu-img reference.

Back in the guest, the kernel notices the new capacity on its own with virtio and virtio-scsi. No reboot, no rescan:

sudo dmesg | tail -3

Both hosts logged the change within a second of the hypervisor command. Ubuntu on virtio-blk:

virtio_blk virtio2: [vda] new size: 62914560 512-byte logical blocks (32.2 GB/30.0 GiB)
vda: detected capacity change from 41943040 to 62914560

Rocky on virtio-scsi:

sd 0:0:0:0: Capacity data has changed
sd 0:0:0:0: [sda] 41943040 512-byte logical blocks: (21.5 GB/20.0 GiB)
sda: detected capacity change from 20971520 to 41943040

If lsblk still shows the old disk size, the controller missed the event. SCSI-backed disks, which covers virtio-scsi, SATA, SAS and most cloud block devices, expose a per-device rescan node, so poke that one device instead of rebooting. Substitute your own disk name:

echo 1 | sudo tee /sys/class/block/sda/device/rescan
echo "- - -" | sudo tee /sys/class/scsi_host/host0/scan

That node belongs to the SCSI layer. Virtio-blk disks such as /dev/vda have no rescan node at all, so on the Ubuntu host /sys/class/block/vda/device/rescan does not exist and there is nothing to poke from inside the guest. If a virtio-blk guest misses the capacity change, the disk has to be detached and reattached at the hypervisor, or the guest restarted.

Install growpart if it is missing

growpart rewrites a single partition entry so it fills the free space that follows it. It is a shell script wrapped around sfdisk, with the awkward parts already handled: it forces the write, backs up the old table first, and pushes the new size into the kernel when the re-read ioctl fails on a mounted root. Check before installing, because cloud images ship with it already:

command -v growpart

Both test hosts answered /usr/bin/growpart straight out of the image. On a minimal or bare metal install you will need the package. Debian and Ubuntu ship it in cloud-guest-utils:

sudo apt update
sudo apt -y install cloud-guest-utils gdisk

RHEL, Rocky Linux and AlmaLinux ship it as cloud-utils-growpart in AppStream, with no EPEL needed. Fedora carries the same package name in its main repository:

sudo dnf -y install cloud-utils-growpart gdisk

Confirm which repository served it if you are auditing a locked-down build:

dnf repoquery --qf '%{name} %{version}-%{release} %{reponame}' cloud-utils-growpart

On Rocky Linux 10 that returns the base channel:

cloud-utils-growpart 0.33-11.el10 appstream

Grow the partition with growpart

Run the dry run first. The -N flag prints the old and new sfdisk dumps on stderr and writes nothing, so you can see exactly which entry is about to move while the CHANGE: line stays on stdout:

sudo growpart -N /dev/vda 1

The disk and the partition number are separate arguments, with no p and no partition suffix. The first line of the dry run is the summary worth reading:

CHANGE: partition=1 start=2324480 old: size=39618527 end=41943006 new: size=60590047 end=62914526

The start sector is identical in both columns. That is the safety property that makes this survivable on a live root: growpart only ever moves the end of a partition, so no data block changes address. Drop the -N to apply it:

sudo growpart /dev/vda 1

A successful run reports CHANGED and the kernel picks up the new partition size immediately:

CHANGED: partition=1 start=2324480 old: size=39618527 end=41943006 new: size=60590047 end=62914526

There is a second repair happening here that is easy to miss. Growing a GPT disk leaves the backup partition table stranded in the middle of the device, and every partitioning tool complains about it. Before growpart, sgdisk -v /dev/vda reported:

Problem: The secondary header's self-pointer indicates that it doesn't reside
at the end of the disk. If you've added a disk to a RAID array, use the 'e'
option on the experts' menu to adjust the secondary header's and partition
table's locations.

Identified 1 problems!

After the run, the same check is clean, because writing the table puts the backup header back at the end of the now larger disk:

No problems found. 4061 free sectors (2.0 MiB) available in 2
segments, the largest of which is 2047 (1023.5 KiB) in size.

Confirm the partition grew, and note that df has not moved yet:

lsblk /dev/vda
df -hT /

The partition is 28.9G, the filesystem inside it is still 19G. This gap is the single most common point of confusion:

NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda     253:0    0   30G  0 disk
├─vda1  253:1    0 28.9G  0 part /
├─vda13 253:13   0 1023M  0 part /boot
├─vda14 253:14   0    4M  0 part
└─vda15 253:15   0  106M  0 part /boot/efi

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/vda1      ext4   19G  2.9G   16G  16% /

Resize an ext4 root filesystem with resize2fs

resize2fs handles ext2, ext3 and ext4 identically. Point it at the device, not the mount point, and give it no size argument so it grows to fill the partition:

sudo resize2fs /dev/vda1

The “on-line resizing required” line is informational, not an error. It means the filesystem is mounted and the kernel is doing the work in place:

resize2fs 1.47.2 (1-Jan-2025)
Filesystem at /dev/vda1 is mounted on /; on-line resizing required
old_desc_blocks = 3, new_desc_blocks = 4
The filesystem on /dev/vda1 is now 7573755 (4k) blocks long.

The space is live immediately:

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/vda1      ext4   28G  2.9G   26G  11% /

Nothing needs editing in /etc/fstab afterwards. The entry references a LABEL or UUID and neither changes when a partition grows, so the mount survives a reboot untouched.

Resize ext4 root partition on Ubuntu using growpart and resize2fs with df output before and after
growpart then resize2fs on a mounted ext4 root, taking it from 19G to 28G with no reboot

Resize an XFS root filesystem with xfs_growfs

The partition step is identical, only the number changes. On the Rocky host root is the fourth partition:

sudo growpart /dev/sda 4

Same CHANGED line, same untouched start sector:

CHANGED: partition=4 start=2463744 old: size=18507743 end=20971486 new: size=39479263 end=41943006

XFS diverges at the filesystem step. Historically xfs_growfs took the mount point rather than the device, and the mount point still works everywhere:

sudo xfs_growfs /

It prints the filesystem geometry, then the one line that matters, the block count change:

meta-data=/dev/sda4              isize=512    agcount=4, agsize=578367 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
data     =                       bsize=4096   blocks=2313467, imaxpct=25
log      =internal log           bsize=4096   blocks=115425, version=2
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 2313467 to 4934907

xfsprogs has accepted a mounted device node again since 5.3, so sudo xfs_growfs /dev/sda4 does the same thing. The mount point is the form that works on every version. Either way the result is live with no remount:

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda4      xfs    19G  1.5G   17G   8% /

The same two commands on the Rocky host, start to finish:

Resize XFS root partition on Rocky Linux with growpart and xfs_growfs showing block count change
growpart then xfs_growfs on a mounted XFS root, 8.4G to 19G

That is the whole difference between the two filesystems in this workflow:

FilesystemGrow commandArgumentOnline growShrink
ext2, ext3, ext4resize2fsdeviceyesoffline only
XFSxfs_growfsmount point or deviceyeslast AG only, experimental

If the volume you are growing sits on a logical volume, or the filesystem is Btrfs, the command set changes again. Those cases are handled in the XFS and Btrfs resize guide.

Grow the partition without growpart

Some hardened images will not let you add packages, and growpart is a shell script that has to be present. The Rocky disk was grown by another 5G before this section, so the numbers below start from 25G rather than the 20G used earlier. The instinct is to reach for parted, which does not work on a mounted root in script mode:

sudo parted -f -s /dev/sda resizepart 4 100%

It fixes the GPT header, then refuses the resize and exits 1 because the partition is in use. The prompt it wants answered never appears under -s:

Warning: Not all of the space available to /dev/sda appears to be used, you can
fix the GPT to use all of the space (an extra 10485760 blocks) or continue with
the current setting?
Fixing, due to --fix
Warning: Partition /dev/sda4 is being used. Are you sure you want to continue?

sfdisk is the one that works. Feeding it , + means “keep the start, extend the size to the end of the disk”, and --no-reread skips the pre-flight check that would refuse to touch a disk the kernel reports as in use:

echo ', +' | sudo sfdisk -N 4 /dev/sda --no-reread --force

It prints the new entry, writes the change, and then warns that the running kernel is still on the old view:

Device       Start      End  Sectors  Size Type
/dev/sda4  2463744 52428766 49965023 23.8G Linux root (x86-64)

The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy
The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or partx(8).
Syncing disks.

That last warning is expected and not a failure. Push the new sizes into the running kernel with partx, which updates partition entries one at a time instead of demanding a re-read of the whole table. Add --nr 4 to touch only the partition you changed:

sudo partx -u /dev/sda

lsblk then shows 23.8G on sda4 and xfs_growfs or resize2fs finishes the job exactly as before. This is also the answer when a tool wrote the table but the kernel never caught up, which the older no tools available to resize disk with gpt note covers from the other direction.

When growpart says NOCHANGE

growpart exits 1 and prints NOCHANGE rather than explaining itself. There are three distinct reasons behind the same word, and the exact wording tells you which one you hit.

NOCHANGE: partition 1 is size 60590047. it cannot be grown

There is no free space after the partition. Either the disk was never extended at the hypervisor, or the guest has not seen the new capacity yet. Compare the disk line and the partition line:

lsblk /dev/vda

If the disk and the partition report the same size, the growth already happened. If the disk is still the old size, go back to the hypervisor step.

NOCHANGE: partition 1 could only be grown by 1024 [fudge=2048]

There is free space, but less than the fudge factor, so growpart decides the change is not worth writing a partition table for. The default fudge is 1048576 bytes, printed here as 2048 sectors, and the number before it is what you would actually gain. Reproduced on a loop device with roughly 512 KiB of free tail:

NOCHANGE: partition 1 could only be grown by 1024 [fudge=2048]

Gaining half a megabyte on a root filesystem is pointless, so the right fix is almost always to grow the disk by a real amount and run it again. Lowering the threshold with --fudge is possible but rarely what you want.

NOCHANGE on a partition that is not last on the disk

This one produces the same “it cannot be grown” wording and catches people out because the disk clearly has free space. growpart can only expand into space that is immediately after the partition, and it will not move a neighbour to get there. A layout with swap sitting behind root hits it every time:

Device         Start     End Sectors Size Type
/dev/loop0p1    2048 4196351 4194304   2G Linux filesystem
/dev/loop0p2 4196352 6293503 2097152   1G Linux swap

After doubling the disk, partition 1 refuses while partition 2 succeeds, because only partition 2 touches the free space:

NOCHANGE: partition 1 is size 4194304. it cannot be grown
CHANGED: partition=2 start=4196352 old: size=2097152 end=6293503 new: size=12580831 end=16777182

The fix is to delete the partition in the way. With swap that is cheap: swapoff -a, remove the swap partition, comment its line out of /etc/fstab, grow root, then recreate swap at the end of the disk or replace it with a swap file. Anything holding real data has to be moved or backed up first.

growpart NOCHANGE messages and resize2fs bad magic number error on an XFS filesystem in Linux
The four failure messages that stop a root partition resize, captured verbatim

Why resize2fs fails on an XFS root

resize2fs is an ext2/3/4 tool and has no XFS support at all, but the error it throws does not say so. Pointed at an XFS partition it reports a superblock problem, which reads like corruption:

sudo resize2fs /dev/sda4

Nothing is damaged. It looked for an ext superblock, found XFS metadata, and gave up:

resize2fs 1.47.1 (20-May-2024)
resize2fs: Bad magic number in super-block while trying to open /dev/sda4
Couldn't find valid filesystem superblock.

Check the type with findmnt -no FSTYPE / and use xfs_growfs. The reverse mistake is harmless in the same way, since xfs_growfs refuses an ext4 target rather than touching it.

Shrinking is a different story

Everything above only grows. Shrinking splits sharply by filesystem, and this is where a lot of resize plans die.

ext4 can shrink, but never while mounted. It needs the filesystem offline, an e2fsck -f first, then resize2fs /dev/vda1 20G, and only then can you shrink the partition to match. For a root filesystem that means booting a live ISO. Shrink the filesystem before the partition, and leave a margin, because a partition smaller than its filesystem destroys it.

XFS is the blunter case. Current xfsprogs can only give back unused space in the last allocation group, so any shrink that would remove an allocation group is refused. Taking this root from 4934907 blocks down to 1000000 crosses several of them:

sudo xfs_growfs -D 1000000 /

xfsprogs 6.16 on Rocky Linux 10 flags the attempt and then fails it:

[EXPERIMENTAL] try to shrink unused space 1000000, old size is 4934907
xfs_growfs: XFS_IOC_FSGROWFSDATA xfsctl failed: Invalid argument

Treat XFS as grow-only in production. To make an XFS root meaningfully smaller you back it up, make a new smaller filesystem, and restore. That asymmetry is worth knowing before you pick a filesystem for a machine whose disk you might want to trim later.

For the common case the sequence is short enough to memorise: grow the disk, run growpart on the disk and partition number, then resize2fs on the device for ext4 or xfs_growfs on the mount point for XFS. Verify with df -hT /. No reboot, no unmount, no fstab edit. If any step reports NOCHANGE, the answer is in the wording, not in a bigger hammer.

Related reading: extending a root filesystem on LVM, extending a KVM virtual machine disk, resizing guest images with virt-resize, and checking disk partitions and usage.

Keep reading

Install KVM and Virt-Manager on Arch Linux Virtualization Install KVM and Virt-Manager on Arch Linux Virsh Commands Cheatsheet for KVM Virtual Machine Management KVM Virsh Commands Cheatsheet for KVM Virtual Machine Management Install KVM on Debian 13 / Debian 12: Complete Guide KVM Install KVM on Debian 13 / Debian 12: Complete Guide The Long Tail of Cloud Waste: Which Workloads Can Move to Shared CPU? Cloud The Long Tail of Cloud Waste: Which Workloads Can Move to Shared CPU? macOS 27 Golden Gate for Developers: What Changed and What Broke Containers macOS 27 Golden Gate for Developers: What Changed and What Broke Allow AWS IAM User to Create Access Keys and Upload SSH Public Keys Cloud Allow AWS IAM User to Create Access Keys and Upload SSH Public Keys

7 thoughts on “Resize Root Partition Without LVM on Linux: ext4 and XFS”

Leave a Comment

Press ESC to close