cd ~/>cat multi-boot-partitioning-merging-btrfs-partitions.md

Multi-boot partitioning / Merging btrfs partitions

#linux#btrfs#partitioning#disk

The problem

p5 (430G) held the Void subvolumes. p6 (457G) held the old shared /home. They were separate btrfs filesystems — different UUIDs, different mount points, no structural relationship. Sharing subvolumes across them required explicit cross-mount fstab entries. Growing either partition meant repartitioning.

The goal was a single unified pool: one UUID, all subvolumes co-located, shared space available to all of them without artificial boundaries.

Why not btrfs device add

btrfs device add adds a second physical device to an existing btrfs filesystem, creating a multi-device pool. The command is:

btrfs device add /dev/nvme0n1p6 /mnt/p5

Do not use this here. Multi-device btrfs pools tie two partitions together at the filesystem level. If either device is unavailable, the pool is degraded or unavailable. On a single NVMe, you get two partition entries serving as one filesystem, with no redundancy benefit and significant added complexity — the pool requires both partitions to be present at every boot, btrfs fi show displays confusing multi-device output, and recovering from a partition table mistake becomes harder.

The cleaner approach: migrate subvolumes by value (copy the data), then resize the remaining partition.

Step 1 — Migrate subvolumes from p6 to p5

Create read-only snapshots of each subvolume on p6:

mount /dev/nvme0n1p6 /mnt/p6
btrfs subvolume snapshot -r /mnt/p6/@home /mnt/p6/@home-snap

Send to p5:

mount /dev/nvme0n1p5 /mnt/p5
btrfs send /mnt/p6/@home-snap | btrfs receive /mnt/p5/

This creates a read-write snapshot at /mnt/p5/@home-snap. Rename it:

# btrfs receive creates a read-only snapshot; convert it
btrfs subvolume snapshot /mnt/p5/@home-snap /mnt/p5/@home
btrfs subvolume delete /mnt/p5/@home-snap

Alternatively, send directly to the target name:

btrfs send /mnt/p6/@home-snap | btrfs receive -e /mnt/p5/

Verify the migration:

btrfs subvolume list /mnt/p5
# @home should appear in the output
du -sh /mnt/p5/@home /mnt/p6/@home
# sizes should match

Step 2 — Snapshot before partition deletion

Before touching the partition table, take snapshots of everything on p5 and confirm fstab is updated to point to the new UUID. This is the point of no return.

# update fstab: replace p6 UUID with p5 UUID for /home
# UUID d7a98795-1604-4baa-ae07-05e29a3ff1e6 for p5

Reboot and confirm /home mounts from p5 correctly.

Step 3 — Delete p6 and extend p5

With p6 unmounted and confirmed unused:

# delete the partition
parted /dev/nvme0n1
(parted) rm 6
(parted) quit

This is destructive and cannot be undone. Verify you are deleting the correct partition number before proceeding.

Extend p5 to fill the reclaimed space:

parted /dev/nvme0n1
(parted) resizepart 5 100%
(parted) quit

resizepart 5 100% extends partition 5 to the end of the available space on the disk. This is safe on btrfs because the filesystem does not yet know about the new partition boundary — it still sees the old size.

Step 4 — Resize the btrfs filesystem

btrfs can be resized online while mounted:

btrfs filesystem resize max /

max tells btrfs to expand to the full device size. This is instantaneous — btrfs does not reformat, it simply updates its internal chunk allocation map to include the new space.

Verify:

btrfs filesystem show /
Label: none  uuid: d7a98795-1604-4baa-ae07-05e29a3ff1e6
        Total devices 1 FS bytes used 480.50GiB
        devid    1 size 887.00GiB used 612.00GiB path /dev/nvme0n1p5

The pool is now 887G — the combined size of the original p5 (430G) and p6 (457G), minus partition alignment overhead.

Why btrfs resize works live

Unlike ext4, which requires unmounting for resize2fs, btrfs manages its space with a chunk-based allocator that can be extended at runtime. The filesystem tracks which physical regions belong to it via the chunk tree. Extending the partition adds new physical space that btrfs can immediately begin allocating. There is no fsck-like consistency pass required.

This is also why shrinking a btrfs filesystem requires a balance first: the chunk allocator must confirm that no data occupies the physical range that will be removed before the partition boundary is moved inward.

Verification checklist

# 1. confirm subvolumes are present
btrfs subvolume list /

# 2. confirm fstab UUIDs
grep btrfs /etc/fstab
# all entries should reference d7a98795-1604-4baa-ae07-05e29a3ff1e6

# 3. confirm pool size
btrfs filesystem show /

# 4. spot-check migrated data
ls /home
diff -r /mnt/p6/@home-snap /home  # if p6 backup is still accessible

# 5. confirm /home is mounted from p5
findmnt /home
# SOURCE should show /dev/nvme0n1p5[/@home]

If all checks pass, delete the read-only snapshot from the source:

btrfs subvolume delete /mnt/p6/@home-snap
umount /mnt/p6

p6 no longer exists. p5 is now the 887G unified pool.

// END OF TRANSMISSION
See you, Space Cowboy.

Connection closed. Returning to terminal...