How do I create a subvolume for existing VMs or folders on the host?

Hi!

I’m new to btrfs. Today, I created a VM in virt-manager (KVM) and realized that maybe I should create a new subvolume for it (+nocow) so it won’t be included in Timeshift snapshots.

Also, is there anything else that should generally be on a separate subvolume? For example, Podman?

What are the steps for this? I’m asking because I don’t want to accidentally mess something up.

> sudo btrfs subvolume list /
ID 256 gen 39201 top level 5 path @home
ID 257 gen 39200 top level 5 path @
ID 258 gen 39054 top level 257 path var/lib/machines
ID 259 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-08_08-32-45/@
ID 260 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-09_09-00-00/@
ID 261 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-10_09-00-00/@
ID 262 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-11_09-00-00/@
ID 263 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-12_09-00-00/@
ID 264 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-13_09-00-00/@
ID 265 gen 38328 top level 5 path timeshift-btrfs/snapshots/2025-06-13_20-00-01/@
ID 266 gen 38939 top level 5 path timeshift-btrfs/snapshots/2025-06-20_19-56-32/@
ID 267 gen 38776 top level 5 path timeshift-btrfs/snapshots/2025-06-27_20-00-00/@
ID 268 gen 38776 top level 5 path timeshift-btrfs/snapshots/2025-06-28_16-32-08/@

> sudo cat /etc/fstab
UUID=e72b6010-fc7f-44e0-9c6e-56e12a313541   /           btrfs   subvol=@,compress=zstd:1       0 0 
UUID=c77234ba-83e1-4342-ac2d-f1140e1cdb5e   /boot       ext4    defaults                       1 2 
UUID=0789-9B8C                              /boot/efi   vfat    umask=0077,shortname=winnt     0 2 
UUID=e72b6010-fc7f-44e0-9c6e-56e12a313541   /home       btrfs   subvol=@home,compress=zstd:1   0 0 
UUID=e48f1a31-e070-4230-a84a-9d30e5cc62ab   /mnt/data   ext4    nofail,users,exec              0 0 

First decide on flat vs nested layout. There’s no technical advantage, btrfs doesn’t care. It’s mainly about organization and rollback behavior. If you rollback with the nested layout, the nested subvolume is elsewhere, so you’d need to manually move it. If you rollback with the flat layout, then the libvirt images subvolume is mounted for you.

The nested approach is something like:
cd test
btrfs sub create images2
cp -r images/* images2
mv images imagesold
mv images2 images

And at some point remove imagesold. You can mv the images subvolume as if it’s a directory if you do a rollback.

For flat layout, you’ll create the libvirtimages subvolume in the top-level of the file system, next to the existing @ and @home subvolumes, and create an appropriate fstab entry to mount it to /var/lib/libvirt/images

Of course if you rollback, far enough you get an old /etc/fstab that will not mount this subvolume! So it’s a catch 22 at first. But eventually all the old @ with old fstab should be gone and then you won’t have this problem if you rollback.

Thank you for your answer! I also found this one a few days ago, which helped me a lot. I didn’t know about the flat vs nested thing, so that definitely helped me.
qemu-virtmanager-in-combination-with-btrfs-and-timeshift

Yeah so another thing that’s funny about Btrfs is that subvolumes get their own st_dev. e.g. if you stat a file in two different subvolumes, the Device field is different. So they are sorta treated as their own file systems in some regards but not others. Subvolumes get their own inode pools. So you will see the same inode number used multiple times on the same btrfs (but not in the same subvolume) which leans more toward the idea it’s a separate file system.

The subvolume on disk is actually a files btree. That’s it. You create a subvolume or make a snapshot, it’s creating a files btree. There’s a pile of other btrees but they are all shared. So not really separate file systems.

The purpose for nodatacow (chattr +C) is not just for no cow performance reasons (making the file behavior overwriting, more like ext4 of XFS, but the btrfs metadata is still cow and can’t be made not cow); but also implied by nodatacow is nodatasum. There are no checksums. This has pros and cons, but if you want to use O_DIRECT, you need it (I think still, as far as I know) because in flight changes in data that are permitted somehow don’t get checksummed again with O_DIRECT? and we end up seeing piles of bogus csum errors with these files.

Anyway I personally use raw files, nodatacow (+C), with the qemu unsafe cache mode. The VM can crash all day long and that VM’s file system is fine. If your host crashes or powerfails while the VM is writing - that’s another story. You’re probably losing data. I’ve never yet lost the guest btrfs though, data loss due to droped writes, but the file system is still consistent due to cow. But I’m not a scientific sample. :joy:

For throw away VMs, I use truncate to create the raw file as a sparse file. For enduring VMs I use fallocate to create the raw file. I never prefill or write zeros or anything like that.

1 Like