Expanding encrypted Btrfs storage by adding an extra partition
What am I talking about?
I’m assuming the following:
- You have a Btrfs partition that you store data on. (I don’t care what data. Don’t tell me.)
- You use LUKS to encrypt it.
- You’re running out of space.
- You have a way of obtaining additional space on your disk but you can’t just simply expand the existing partition.
If your situation differs, the rest of this post may be much less helpful.
Disclaimer
You’re following this guide at your own risk and I take no responsibility for any data loss, physical damage, or Portal Gun malfunctions. It could be risky.
Do have a backup of your data handy in case things go awry (a Btrfs snapshot does not count). Do have a bootable USB drive ready in case something goes really wrong.
Okay, here we go…
Make more space
I leave this part as an exercise for the reader, since circumstances may vary.
In my specific scenario, I had a dual-boot set-up with Windows and Ubuntu and I started running out of space for the latter1. I couldn’t extend the existing Btrfs partition any more—the only way to obtain additional storage space was to shrink the Windows partition.
And so I did.
I ended up shrinking it by about 200 GB and created a new partition in the newly emptied space, leaving me with:
% sudo parted /dev/nvme0n1 print
Number Start End Size File system Name Flags
...
3 290MB 274GB 273GB ntfs Basic data partition msftdata
6 274GB 483GB 210GB Linux data (second device)
5 483GB 1023GB 540GB Linux data
...
Here 5
(or /dev/nvme0n1p5
) is my main Linux data partition, and 6
(or /dev/nvme0n1p6
) is the freshly added one.
Encrypt the new partition
It’s now time to encrypt the new partition. Using the same passphrase and keyfile that were used to encrypt the existing partition is likely to yield best results. This way, it should be enough to type the passphrase once at boot time to decrypt both.
Hence, we start with:
% sudo cryptsetup luksFormat /dev/nvme0n1p6
Which—if you do use a keyfile—should be followed by:
% sudo cryptsetup luksAddKey /dev/nvme0n1p6 /etc/luks/your.keyfile
where /etc/luks/your.keyfile
is the path to your existing keyfile.
Update crypttab
We now need to update /etc/crypttab
to ensure that the new partition is correctly decrypted during the boot sequence.
We’ll need the UUID of the new partition, and we can get it with blkid
2:
% blkid /dev/nvme0n1p6
/dev/nvme0n1p6: UUID="66cf1bcd-f756-4aa2-917f-1f25fa313731" TYPE="crypto_LUKS" PARTLABEL="Linux data (second device)" PARTUUID="5cc178a6-1049-4304-9473-61d3dd871e93"
Then we add a new line to /etc/crypttab
(I’m following the convention to append _crypt
to the device name, but use what makes sense for you), leaving us with something like this:
nvme0n1p5_crypt UUID=5efbd82c-29c3-443a-b028-ff21d91f78d0 /etc/luks/your.keyfile luks
nvme0n1p6_crypt UUID=66cf1bcd-f756-4aa2-917f-1f25fa313731 /etc/luks/your.keyfile luks
And now it’s a good time to test this configuration before proceeding further—you don’t want to discover that your configuration is incorrect during boot. Run:
% sudo cryptdisks_start nvme0n1p6_crypt
* Starting crypto disk...
* nvme0n1p6_crypt (starting)...
* nvme0n1p6_crypt (started)... [ OK ]
This should decrypt /dev/nvme0n1p6t
and create a new device at /dev/mapper/nvme0n1p6_crypt
.
If you got an error instead, make sure that you used the correct UUID2.
If you did not get any errors, you can proceed to update initramfs
. This step is crucial if you want to avoid surprises at boot time (such as Btrfs reporting a missing device):
% sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-5.19.0-1012-lowlatency
Add the new Btrfs device
This is where we leverage one of the powers of Btrfs. Namely the one to create a single filesystem spanning multiple devices. (Note: we’re not setting up RAID here but that is an option as well.)
In the following scenario I’m assuming that /dev/mapper/nvme0n1p5_crypt
is mounted on /
. If your existing partition is mounted elsewhere, use that path instead. This is easy enough to check with something along the lines of:
% mount|grep nvme0n1p5_crypt
/dev/mapper/nvme0n1p5_crypt on / type btrfs (rw,relatime,ssd,space_cache,subvolid=256,subvol=/@)
...
We can add the second partition now3:
% sudo btrfs device add /dev/mapper/nvme0n1p6_crypt /
And let’s verify that it worked. You should be able to see something like this:
% sudo btrfs filesystem show
Label: none uuid: f35c2a2e-db68-49a3-98a3-634eba37de92
Total devices 2 FS bytes used 467.38GiB
devid 1 size 502.62GiB used 502.62GiB path /dev/mapper/nvme0n1p5_crypt
devid 2 size 195.30GiB used 0.00B path /dev/mapper/nvme0n1p6_crypt
And your total available space should increase:
% df -h
Filesystem Size Used Avail Use% Mounted on
...
/dev/mapper/nvme0n1p5_crypt 698G 468G 229G 68% /
...
/dev/mapper/nvme0n1p5_crypt 698G 468G 229G 68% /swap
/dev/mapper/nvme0n1p5_crypt 698G 468G 229G 68% /home
...
And you’re (hopefully) done.
Reboot
Now for the big test: reboot your system. Once you reboot, sudo btrfs filesystem show
should display the same info.
If something goes wrong, you’re likely to end up in an (initramfs)
prompt where you can manually decrypt the new partition using the commands above, continue the booting process, and then proceed to fix your configuration.
-
I stupidly thought that 1 TB was a lot of space, so splitting it in half between Windows and Ubuntu would be completely reasonable in case I ever needed Windows to run specific VSTs or play video games. ↩︎
-
If you’re getting an outdated value (e.g. if you don’t see
TYPE="crypto_LUKS"
or the partition can’t be found in the following steps), try adding-p
toblkid
to bypass the cache, or trylsblk -o name,uuid
. ↩︎ ↩︎ -
Note that it has to be decrypted and the
/dev/mapper/nvme0n1p6_crypt
device must exist. You can achieve that with eithercryptdisks_start nvme0n1p6_crypt
in one of the previous steps orcryptsetup open /dev/nvme0n1p6 nvme0n1p6_crypt
. ↩︎