Sometimes, you need to resize your partitions on Linux. And this may not be as easy as you think...
When I installed Debian on my work computer a few months ago, the wizard suggested creating 2 separate partitions: one for root (with the OS itself), another for home (with my personal data). Alas! The suggested sizes (that I naively accepted) were highly unsuitable and a month later, the root partition (only 40 Go) was full. I had to resize them. It turns out that now, 5 months later, the home is full (I really generate a lot of data for my current project). It's time to resize my partitions again but this time I will keep track of the entire procedure in this article. Who knows when I will have to resize them one more time...
Current State and Goal
My computer as a single encrypted SSD disk:
% lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 3.6T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot/efi
├─nvme0n1p2 259:2 0 488M 0 part /boot
└─nvme0n1p3 259:3 0 3.6T 0 part
└─nvme0n1p3_crypt 253:0 0 3.6T 0 crypt
├─debian--vg-root 253:1 0 2.6T 0 lvm /
├─debian--vg-swap_1 253:2 0 976M 0 lvm [SWAP]
└─debian--vg-home 253:3 0 1T 0 lvm /home
fdisk -l
shows similar information.
As I said, my home partition is full. However, the root partition is almost empty:
% df -h | grep debian
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/debian--vg-root 2.6T 202G 2.3T 8% /
/dev/mapper/debian--vg-home 1007G 937G 19G 99% /home
My goal is to resize the partitions so that root is 1T and home occupies the rest of the disk. This will be a 2-step process: first, shrink root ; second, extend home.
Apparently, I can't do this from the graphical disk utility (note "Edit Partition..." and "Resize..." being greyed out):
It's time to open a terminal and input some weird commands!
Shrinking Root
Because I want to resize the root partition, it seems that I can't directly work from my installed Debian. I put my hands back on my bootable USB drive (the one I used to install Debian in the first place) and I reboot my computer on it. It doesn't provide a live Debian mode, so I have to use the rescue mode to get a shell:
Yes, I took pictures on my laptop's screen.
Once I have configured my language and keyboard layout, and skipped the network configuration, I can finally enter the rescue mode for real:
I have to enter my passphrase because my disk is encrypted, and I now have a shell. Hooray!
Resizing a "partition" is not a single-step operation. Indeed, there are in fact a logical volume and a filesystem on top of it. Both need to be resized in the correct order.
First, reduce the filesystem with the resize2fs
command. In this first step, I use a temporary size, not the desired size. It must be big enough to hold all the files but smaller that the desired size. The output df -h
above shows that 202G are used on root. The desired size is 1T. Hence, 300G seems fine.
resize2fs /dev/mapper/debian--vg-root 300G
I am asked to check the filesystem first with e2fsck -f /dev/mapper/debian--vg-home
. Once the check and possible fixes are done, I can call resize2fs
again.
Second, reduce the volume to the desired size (1T) with the lvreduce
command:
lvreduce -L 1T /dev/vgroot/lvroot
Third (and last), extend the filesystem to use the entire volume. This is done by passing only the volume to resize2fs
because if the "size parameter is not specified, it will default to the size of the partition".
resize2fs /dev/vgroot/lvroot
This is how it looks like in real life:
Note that step 3 may be performed directly in step 2, because lvreduce
has an option that seems to do it:
-r
,--resizefs
Resize underlying filesystem together with the logical volume usingfsadm
.
To be honest, I wasn't really keen to try on my work computer, I can't afford any data loss. So I stuck a procedure that I knew to work, but I encourage you to test it 😅
Checking Root
Now, I shut down my computer, I remove the USB drive, I reboot to my actual system, and I'm glad to see that it still works 😄
I can check the state of my disk:
% df -h | grep mapper
/dev/mapper/debian--vg-root 1008G 202G 756G 22% /
/dev/mapper/debian--vg-home 1007G 937G 19G 99% /home
% lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 3.6T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot/efi
├─nvme0n1p2 259:2 0 488M 0 part /boot
└─nvme0n1p3 259:3 0 3.6T 0 part
└─nvme0n1p3_crypt 253:0 0 3.6T 0 crypt
├─debian--vg-root 253:1 0 1T 0 lvm /
├─debian--vg-swap_1 253:2 0 976M 0 lvm [SWAP]
└─debian--vg-home 253:3 0 1T 0 lvm /home
The root partition is fine. Of course, the home partition is unchanged. Let's resize it now.
Extending Home
This time, I don't need to boot on my USB drive.
First, extend the volume with the lvextend
command. I don't have to calculate the remaining size, I just ask to add 100% of the free space of the disk to the volume:
% sudo lvextend -l +100%FREE /dev/mapper/debian--vg-home
Size of logical volume debian-vg/home changed from 1.00 TiB (262144 extents) to <2.64 TiB (691219 extents).
Logical volume debian-vg/home successfully resized.
Note that the option is -l
(lowercase) not -L
(uppercase), unlike with lvreduce
.
Second (and last), resize the filesystem to use the entire volume (same as last operation for root):
% sudo resize2fs /dev/mapper/debian--vg-home
resize2fs 1.47.0 (5-Feb-2023)
Filesystem at /dev/mapper/debian--vg-home is mounted on /home; on-line resizing required
old_desc_blocks = 128, new_desc_blocks = 338
The filesystem on /dev/mapper/debian--vg-home is now 707808256 (4k) blocks long.
Checking Home
Finally, I have reached my goal:
% df -h | grep mapper
/dev/mapper/debian--vg-root 1008G 202G 756G 22% /
/dev/mapper/debian--vg-home 2.6T 937G 1.6T 37% /home
% lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 3.6T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot/efi
├─nvme0n1p2 259:2 0 488M 0 part /boot
└─nvme0n1p3 259:3 0 3.6T 0 part
└─nvme0n1p3_crypt 253:0 0 3.6T 0 crypt
├─debian--vg-root 253:1 0 1T 0 lvm /
├─debian--vg-swap_1 253:2 0 976M 0 lvm [SWAP]
└─debian--vg-home 253:3 0 2.6T 0 lvm /home
Conclusion
I have to be honest, it was quite scary to resize partitions 😱 Of course, I backed up my important data, but I can't really back up a full system with all the installed tools and settings I have as a software developer on my machine.
At the end of the day, I left this uncomfortable situation:
Before:
% df -h | grep mapper
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/debian--vg-root 2.6T 202G 2.3T 8% /
/dev/mapper/debian--vg-home 1007G 937G 19G 99% /home
And I have reached with much more cozy one:
% df -h | grep mapper
/dev/mapper/debian--vg-root 1008G 202G 756G 22% /
/dev/mapper/debian--vg-home 2.6T 937G 1.6T 37% /home
I hope this was the last time I had to resize my partitions on this computer! 😁
Top comments (4)
A few thoughts.
Root is still way over-allocated
...First off: Why? By which I mean, why waste a full terabyte on the root partition? It's only using 202GB right now. How much bigger do you expect it to grow???
Data is consumed on Linux filesystems in the user space — the home partition. The root partition might grow slightly over time, but once the system has been up and running and all of the software you need is installed, it's unlikely to experience significant growth beyond its current size. If your root partition is currently 202GB, 250GB is the maximum I'd allocate to it, because it's unlikely to grow beyond that.
You say, "I hope this was the last time I had to resize my partitions on this computer!", but by continuing to waste almost 800GB of empty space on a root allocation, you're making it more likely that it won't be the last time. Eventually, you'll probably want more of that space for home.
Counter-intuitively, it's a mistake to fully allocate an LVM volume-group up front
As you've seen, shrinking logical volumes is a pain in the neck. But growing them is a simple, convenient, on-demand action that can be performed at any time — online, without even unmounting the partition! Heck, you can grow a logical volume while you're actively hammering it with data transfers, and the only consequence will be that both operations take slightly longer to complete.
Because LVM offers on-demand, near-instant ad-hoc growth, the best way to manage volume allocations is to wait until you need the space. You say,
...The mistake you made originally wasn't accepting the wizard's recommendation of a 40GB root partition, it was giving the entire rest of the disk to the home partition.
You're working with a 3.6TB physical volume. If it were me, I'd initially have accepted that 40GB root allocation, and allocated maybe a 1TB home. I'd have left the rest of the space in the volume group completely unallocated.
Then, when the root proved far too small, I'd simply have allocated another 100GB to it with
sudo lvresize -r -L +100G debian-vg/root
.When the first 1TB of
/home
filled up,sudo lvresize -r -L +500G debian-vg/home
to give it more wiggle room. When that runs out, just do it again.If you start with reasonable size allocations and leave yourself lots of unallocated extents in the volume group, you can let the logical volumes grow gradually and organically as they fill up their current allocations.
The absolute worst position to be in is the one you've set up at every point in this process: fully-allocating your volume group so it's depleted of unallocated extents. Until you hit that point (which you should try to hold off as long as humanly possible), LVM's dynamically-allocated extent mapping is incredibly flexible. Once it's fully allocated, you've basically given up almost every advantage LVM has over dumb, pre-LVM static partitioning.
ALWAYS use the
-r
option tolvresize
(orlvextend
/lvreduce
, but who uses those anymore?)You say you're wary of it, I'm telling you: Do not be. Rely on it. There's no reason to ever fiddle with manual
resize2fs
runs to make partitions fit inside logical volumes. Just let LVM resize the partition to fit the logical volume at every step along the way.And when growing LVs into unallocated extents, it reduces the entire operation to a single, atomic
lvresize
command.Hello
Thanks for your enlightening comment.
1/Root is still way over-allocated
I don't know. How could you know? 3 months later, I now have:
I may be able to make some cleanup to regain some space, but I don't want to do this kind of cleanup often if I can afford to do occasionally (or at least, to afford to delay the moment where I will have to understand what this cleanup would be).
1T was probably too much. However, 250G would have been too little. Maybe 500G could be a good point.
2/ Counter-intuitively, it's a mistake to fully allocate an LVM volume-group up front
I like the approach you are showing. Next time I set up a new machine, I will try it.
I have just run Debian installer inside a VM to see again how the wizard proposes to arrange the disk. At some point, you have to choose between "Use entire disk" or "Manual". If you select the first option, you are then asked if you want a separate partition for
/home
. If you don't know your proposed approach, well, you end up with the rest of the disk for the home partition.I have been wondering whether having a separate partition for
/home
was even a good idea. What is your opinion in this?3/ ALWAYS use the -r option to lvresize
I was already convinced to use it next time. Thank you for reassuring me, though :D
That's insane! Is there stuff writing to the machine's root partition that should be using
/home
instead?My experience is with Fedora, so not everything will be equivalent, but... this desktop system I'm posting from (actual minitower; I own a laptop, but use it only when away from my desk) has a root volume that was created in 2013, has been through 11 years of twice-yearly Fedora updates, and currently has 9513 packages installed from Fedora's repos (twice as many as any of my other systems). It's 112.4G, using 71.7G (68%), and other than those 2× a year when it grows by another 5-12G staging the upgrade package set for the next Fedora release, any changes in size are so glacial they're difficult to even notice.
A year or two after its initial creation, I extended its size from the starting allocation of 70G (which was already proving slightly too tight) to its current excessive size. (I overshot, intending to add ~10G, but I was working in extent counts instead of size units for some stupid reason, and I did the math wrong.) I don't know when, or if ever, I'll manage to fill up all 30G of empty space there.
So, normally root filesystems don't grow by all that much or all that fast, at least in my experience. If yours has grown so explosively because you've just continued to install a lot more "stuff", then that's one thing, and you're clearly a power user who needs a lot of space.
But if it's grown because something is chewing up scads of space in
/usr
or/var
that maybe shouldn't be, that's another thing entirely. (On Fedora, virtual machine images used to burn tons of/var
space. But since GNOME Boxes, they've been relocated to the user's$HOME/.local/share/
space where they belong. Docker and other container-system images, OTOH, still live in/var
, and those can burn through space like crazy if you don't prune them regularly.)Anyway, I can definitely get behind the "I don't want to have to babysit it" approach, so if your root is still growing that quickly then I agree, 500G sounds about right to give it some runway.
I'm old enough to remember the pre-Linux "standard UNIX filesystem layout", where the root partition was just
/bin/
,/sbin/
,/etc/
, and a bunch of empty directories that served as mount points./var
was a separate partition,/usr/
was a separate partition — heck, sometimes JUST/var/mail/
or/var/db
was a separate partition, on a mailserver or database server — and so on.All of that was terrible. Managing allocations was a nightmare, and it was practically a full-time job for at least one sysadmin, just making sure that every machine had the proper disk allocation for each of its many, many mounted filesystems. Especially since this was back in the days of static filesystem allocations, where even growing a partition meant system downtime.
Consolidating all of the system-installable bits into a single filesystem was absolutely the correct way to go there, even though in the world of LVM it would've been a lot more manageable than it used to be. There's just no reason for that.
But I'm still strongly in favor of the root/home split, personally. For a few reasons:
Since we're talking about disk allocations, storage management, and needing more space for
/home
, it's much less bad to fill up your/home
partition if that doesn't mean you've also filled up your root fs. (Once I get up to that line where it just isn't enough space, I seem to run out of space in/home
weekly, no matter how much cleanup I do.)Yeah, Unix filesystems since the dark ages have had reserved block allocations, so that the filesystem appears 5% smaller to non-root processes, which will register it as "full" even though there's actually still a bit of space left. That does help prevent system crashes or login failures from a "full" root FS, and back in the days of rotating magnetic media, it also helped keep the disk from thrashing as fragmentation exploded when stuffing blocks into every last iota of a disk's free space. But 5% is both over- and under-kill, since on a multi-terabyte volume it's too much space to reserve for root, but at the same time it's less free space percentage-wise than you want on your root volume normally.
If you're disciplined about it, it handily separates "user-created data" from "generic data". I'm very disciplined — I never run "sudo make install" or anything like that, on my systems. My rule is that everything that gets written to my machines' system volume is managed by the packaging system. (DNF/rpm, in fedora's case, or apt/dpkg in Debian/Ubuntu.) If I need to install something that's not packaged by the distro, I either package it up myself and then install it with
dnf
, or I build it with an install prefix of$HOME/.local
and install it there.As a result, if I ever had a catastrophic failure on a machine where I had to reinstall the OS, I could re-create it completely with nothing more than a backup of the
/etc
directory, and a list of all the packages that were installed at the time (a list I dump in nightly). There's no other unique data anywhere on the root volume that can't easily be reproduced with a download or local package install.It's a requirement for immutable-os setups. Probably not a Debian thing, and I don't know if Canonical has anything along these lines; they probably do. Fedora's is called Silverblue. But it's also the same thing Apple has been doing with macOS since I think the 10.15 series: The base OS itself is stored on a volume that's mounted read-only at boot, so that no changes can be made while the system is running — they have to be staged and performed during a restart.There's usually an overlay mount to make certain spaces (like
/var/
, or/Library
on macOS) writable, but all of the/usr
files and other system bits are read-only. I don't run Silverblue, but there's a bunch of sense to it, as it's a good line of defense against rootkits, fatfingeredsudo
commands, and general "oops, I really shouldn't have done that" moments when acting as root on a running system.Related to #2, it makes migration between distros/installs easier. Since VM tech matured I don't see the point of dual-booting machines anymore, but if I had to install a different distro on one of my machines for some reason, I'd create a new LV, install the new system there, and mount my existing
/home
volume to it. Ditto if I had to reinstall Fedora for some reason.Heck, when I was dual-booting Windows, I toyed with keeping my Linux
/home/
on an NTFS partition so I could share it between all of my OSes. (I came to my senses and didn't actually do that. Though at one point I did have/boot
on NTFS, so that I could change the default Grub selection from Windows. I stopped doing that because Fedora's upgrade process choked on it twice a year.)Replying late, sorry.
Very interesting comment again!
It turns that Docker is guilty for consuming these gigabytes:
I will do some cleanup!