Btrfs Subvolumes

I’m creating Btrfs subvolumes to use with backups. The first three subvolumes were created when I installed. The rest were created after.

ID 256 gen 22669 parent 5 top level 5 path home
ID 257 gen 22647 parent 5 top level 5 path root
ID 262 gen 22015 parent 376 top level 376 path var/lib/machines
ID 369 gen 21744 parent 257 top level 257 path opt
ID 370 gen 22517 parent 257 top level 257 path usr/local
ID 371 gen 22514 parent 257 top level 257 path etc
ID 372 gen 21991 parent 257 top level 257 path root
ID 374 gen 22647 parent 376 top level 376 path var/cache
ID 375 gen 22608 parent 376 top level 376 path var/tmp
ID 376 gen 22662 parent 257 top level 257 path var
ID 378 gen 22172 parent 257 top level 257 path srv

A few questions:

  1. The first two subvolumes appear in my fstab. The rest do not. I’m wondering why “home” was added to my fstab but “var/lib/machines” was not.
  2. Should I create fstab entries for the subvolumes I created by hand?
  3. I have two subvolumes that are both named “root”. The first is for “/”, the other is for “/root”? Is it okay these two subvolumes share the same name? What naming convention is typically used for these two mount points?
  4. Running btrfs subvolume list -o / shows every subvolume except “home”. Why is that? Should I make “home” a child of “root” like the others? (Or should I make the other subvolumes like “home”?)
1 Like

btrfs can be like folders.

When the physical location of the subvol is correct, then we do not need an entry in /etc/fstab to adjust the mount point.

  1. When location of subvol is correct, do not need /etc/fstab entry to specified a mount point
  2. If you want those subvols to be mounted by default, you need /etc/fstab entry. (or use systemd-mount)
  3. Not sure. I am not sure why you can have two “root” subvol at the same path. when you mount with -o subvol=root, which subvol will actually got mounted? You can always mount with subvolid= (257 for the first, 372 for the second). I will rename one of it to prevent confusions.
  4. It is becase -o specific to list only subvol below the given mount point.

btrfs subvols can be created using flat or hierarchical way (usually mix of them) . As long as you can access them at the required mount point, then all is fine.

On your item 4, I prefer the flat subvolume layout, where every subvolume is a direct child of the btrfs root subvolume (which is not to be confused with the Linux / directory). Nested subvolumes are not backed up when their parents are snapshotted, nor are they restored when the parent is restored. They can be individually snapshotted, though, but you have to remember to do it. And, if there are several levels of children, it becomes messy.

Every btrfs filesystem has a root subvolume, identified as ID=5. They can also be addressed as ID=0.

Here is a clearer explanation on the BTRFS Wiki: SysadminGuide - btrfs Wiki

4 Likes

The installer will add subvolumes to fstab for any subvolumes used in the installation; either created by the installer or by the user or subvolumes reused by the user (e.g. reusing the home subvolume for an otherwise clean installation with a new root subvolume).

var/lib/machines was created by systemd, not the installer, and it is a so-called “nested” subvolume as are all the other subvolumes you created. Nesting happens anytime the subvolume is not located in top level ID 5.

https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Layout

Depends on what you’re trying to achieve. Nested subvolumes don’t need to be added to fstab. But they also aren’t included when making snapshots of their parent (snapshots end at subvolume boundaries). And rollback is complicated because if you rollback to a snapshot, it won’t automatically contain all of these subvolumes you’ve created. Generally speaking, it’s advised to create subvolumes in the top level of the file system (ID 5) and use fstab to mount them into their proper location. If you created this layout in the installer’s Custom or Advanced Custom UI, it would use this so called “flat” layout. This is really an organizational preference, Btrfs itself doesn’t care and establishes no rules.

Btrfs doesn’t care what they’re named because behind the scenes it’s interacting with subvolumes based on the ID anyway. So you can name them whatever you want, just like a directory. Either referencing them by ID or name, it’s unambiguous

subvolid=257 is the same as subvol=root
subvolid=372 is the same as subvol=root/root

It might be more obvious using btrfs sub list -t /

Because / means subvolid=257 subvol=root and the home subvolume isn’t nested inside root like all the other subvolumes. The -o option only shows subvolumes that are below the path you specify.

Depends on what you’re trying to achieve. I personally only create subvolumes in the top-level of the file system, i.e. at mkfs time, a hidden subvolume without a name with ID 5 is created and referred to as either “top level of the file system” or also “fs tree”. If you mount this btrfs normally, it mounts this subvolume. That’s why you will see what appears to be two directories named home and root in it, but those are subvolumes. You can ls -li to see their inode numbers are 256, which is reserved for subvolumes.

Some utilities like snapper make use of the default subvolume via btrfs subvolume set-default command which can change what subvolume is mounted when -o subvol/subvolid options are not used. In this case the way to mount the top level of the subvolume is either:

mount -o subvolid=5 /dev/ /mnt
mount -o subvol=/ /dev/ /mnt

There is also an ID 0 aliased to ID 5. So you can use subvolid=0 if that’s easier to remember (that’s the point of the alias, because remembering ID 5 is a bit esoteric.)

2 Likes

Thanks for the feedback!

Here are the current subvolumes:

ID 256 gen 23861 top level 5 path home
ID 257 gen 23826 top level 5 path root
ID 262 gen 23700 top level 381 path var/lib/machines
ID 374 gen 23824 top level 381 path var/cache
ID 375 gen 23861 top level 381 path var/tmp
ID 379 gen 23747 top level 5 path opt
ID 380 gen 23794 top level 5 path srv
ID 381 gen 23861 top level 5 path var
ID 382 gen 23861 top level 257 path root/etc
ID 383 gen 23794 top level 5 path rootroot
ID 384 gen 23793 top level 5 path usrlocal

Here is my fstab:

UUID=****  /           btrfs   subvol=root,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /boot       ext4    defaults 1 2
UUID=****  /boot/efi   vfat    umask=0077,shortname=winnt 0 2
UUID=****  /home       btrfs   subvol=home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /opt        btrfs   subvol=opt,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /srv        btrfs   subvol=srv,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /var        btrfs   subvol=var,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /root       btrfs   subvol=rootroot,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=****  /usr/local  btrfs   subvol=usrlocal,compress=zstd:1,x-systemd.device-timeout=0 0 0

The plan is to back up home, root/etc, rootroot, usrlocal, var, srv, and opt. I DO NOT plan to back up root, var/cache, var/tmp, or var/lib/machines.

I wanted to create a top level volume for /etc too but isn’t obvious how to do that.

Suggestions?

I probably wouldn’t put /etc on its own subvolume separate from its sysroot, because you have no way of rolling back without moving it. Systemd needs /etc/ so early during startup, that it’s not possible to depend on systemd’s fstab generator creating the mount units in time. So while a nested subvolume for /etc works, because it doesn’t have to be mounted by systemd, if you were to do a rollback to a snapshot of root, you have no etc in that subvolume and thus boot will fail.

Essentially the things that are tied to each other are /usr /var and /etc and I put those in their own subvolume called root34 (i.e. the sysroot for Fedora 34) and mount it at /. The reason /var and /usr are tied to each other is because the rpm database is in /var and things get super messy if you’ve got an rpm database that doesn’t exactly reflect everything in /usr and probably /var for that matter.

I wanted to ask:

ID 372 gen 21991 parent 257 top level 257 path root

For subvolid=372, the path is simply “root” . Is this path relative to the “default subvol” or relative to the subvolid=5 ?

For subvolid=372, the path is simply “root” . Is this path relative to the “default subvol” or relative to the subvolid=5 ?

For 372, the path “root” was relative to 257. I recreated it at the top level and renamed it “rootroot”.

1 Like

after readming btrfs subvol list --help, I notice that option -a can be used to avoid confusions.

btrfs subvol list -a /
ID 267 gen 936 top level 5 path <FS_TREE>/sg2T-2021-05-13T09:46+08:00/p3.btrfs.receive/home
ID 269 gen 956 top level 5 path <FS_TREE>/sg2T-2021-05-13T09:46+08:00/p3.btrfs.receive/root
ID 409 gen 101782 top level 5 path <FS_TREE>/iso
ID 417 gen 108543 top level 5 path <FS_TREE>/var_log35b
ID 418 gen 108543 top level 5 path <FS_TREE>/home35b
ID 419 gen 108540 top level 5 path <FS_TREE>/root35b
ID 431 gen 97544 top level 5 path <FS_TREE>/VMs
ID 432 gen 95477 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol34
ID 433 gen 92810 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol35
ID 665 gen 108146 top level 419 path root35b/ostree/deploy/fedora/var/mnt/test
ID 666 gen 108146 top level 665 path <FS_TREE>/root35b/ostree/deploy/fedora/var/mnt/test/test

I’ve reworked everything to keep /usr, /etc, and /var on a single subvolume called “root34”.

Here are the current subvolumes:

ID 256 gen 24474 top level 5 path <FS_TREE>/home
ID 257 gen 24474 top level 5 path <FS_TREE>/root34
ID 262 gen 24417 top level 257 path <FS_TREE>/root34/var/lib/machines
ID 374 gen 24432 top level 257 path <FS_TREE>/root34/var/cache
ID 375 gen 24463 top level 257 path <FS_TREE>/root34/var/tmp
ID 379 gen 23747 top level 5 path <FS_TREE>/opt
ID 380 gen 24417 top level 5 path <FS_TREE>/srv
ID 383 gen 24410 top level 5 path <FS_TREE>/roothome
ID 384 gen 24410 top level 5 path <FS_TREE>/usrlocal

Here is my fstab:

UUID=**** /          btrfs subvol=root34,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=**** /boot      ext4  defaults 1 2
UUID=**** /boot/efi  vfat  umask=0077,shortname=winnt 0 2
UUID=**** /home      btrfs subvol=home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=**** /opt       btrfs subvol=opt,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=**** /srv       btrfs subvol=srv,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=**** /root      btrfs subvol=roothome,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=**** /usr/local btrfs subvol=usrlocal,compress=zstd:1,x-systemd.device-timeout=0 0 0

I plan to back up root34, home, roothome, usrlocal, srv, and opt. I plan to exclude var/cache, var/tmp, or var/lib/machines.

Let me know what you think.

Seems fine.

On one laptop I add to “root” and “home” subvolumes with:

UUID= /var/log                btrfs   noatime,subvol=varlog34     0 0
UUID= /var/lib/flatpak        btrfs   noatime,subvol=varlibflatpak     0 0

And on another laptop it’s

UUID= /var/log                btrfs   noatime,subvol=varlog34     0 0
UUID= /var/lib/libvirt/images     btrfs   noatime,subvol=libvirtimages     0 0

Logs I don’t want snapshot or rolled back. I want the logs to always go forward. Same reasoning for flatpaks and libvirt VM images, and also they have their own means of reverting to previous states. If you’re familiar with virt-manager, Boxes, and qcow2 snapshots then that’s probably the thing to use for that, rather than having the file system incidentally snapshot such things. By creating subvolumes for them, they’re excluded from snapshots of root.

[Since I created the subvolume for libvirt images outside the installer, I did remember to chattr +C on it. Ordinarily folks clean install, and upon libvirt first creating and starting the /var/lib/libvirt/images storage pool, it sets chattr +C. But it’s a one time event, so if you replace it after libvirt has created the pool, it doesn’t check or change the file attribute for this path later on.]

Also, the reason one of those subvolumes is varlog34 is because that laptop has multiple Fedora releases on it, on the same Btrfs, each in their own subvolume. So Fedora 33 has its own varlog33 subvolume, likewise Fedora 34 has its own varlog34.

1 Like

Well, when I was installing F34 using the advanced custom UI, I got an error message when I tried to create the subvolume /root as it said that one already existed, tried twice with same outcome. As a result, I don’t have a “root” subvolume in my system:

sudo btrfs subvolume list -o /                                                                                                                                            
ID 256 gen 26026 top level 5 path home
ID 258 gen 26027 top level 5 path var
ID 259 gen 25917 top level 5 path usr-local
ID 260 gen 25769 top level 5 path srv
ID 261 gen 24862 top level 5 path opt
ID 262 gen 18311 top level 5 path boot-efi
ID 279 gen 26023 top level 5 path .snapshots

I used openSUSE’s layout as a guide. So I would dare to say that either btrfs does care about naming or if as you said it doesn’t, then something’s wrong with Anaconda handling that.

1 Like

btrfs subvols are similar to folders:

  • cannot have two same entry having the same name under the same parent
  • can be renamed
  • can be moved

so if mount using subvol= option, it needs updated after move/rename. if using subvolid= option, then no changes need in mount options.

btrfs subvol list -o / only list those subvols that is under the currently mounted ‘/’.

btrfs subvol list -o /
ID 432 gen 109787 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol34
ID 433 gen 109787 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol35
ID 665 gen 109051 top level 419 path root35b/ostree/deploy/fedora/var/mnt/test
ID 668 gen 127153 top level 419 path root35b/ostree/deploy/fedora/var/lib/libvirt/images/test

btrfs subvol list -o / should give a more clear picture.

btrfs subvol list -a /
ID 267 gen 936 top level 5 path <FS_TREE>/sg2T-2021-05-13T09:46+08:00/p3.btrfs.receive/home
ID 269 gen 956 top level 5 path <FS_TREE>/sg2T-2021-05-13T09:46+08:00/p3.btrfs.receive/root
ID 409 gen 101782 top level 5 path <FS_TREE>/iso
ID 417 gen 349981 top level 5 path <FS_TREE>/var_log35b
ID 418 gen 316194 top level 5 path <FS_TREE>/home35b
ID 419 gen 349982 top level 5 path <FS_TREE>/root35b
ID 431 gen 110052 top level 5 path <FS_TREE>/VMs
ID 432 gen 109787 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol34
ID 433 gen 109787 top level 419 path root35b/ostree/deploy/fedora/var/subvols/subvol35
ID 665 gen 109051 top level 419 path root35b/ostree/deploy/fedora/var/mnt/test
ID 666 gen 109051 top level 665 path <FS_TREE>/root35b/ostree/deploy/fedora/var/mnt/test/test
ID 668 gen 127153 top level 419 path root35b/ostree/deploy/fedora/var/lib/libvirt/images/test

This is what I ended up with:

ID 256 gen 30201 top level 5 path home
ID 257 gen 30201 top level 5 path root34
ID 262 gen 27610 top level 5 path varlibmachines34
ID 374 gen 30201 top level 5 path varcache34
ID 375 gen 30201 top level 5 path vartmp34
ID 379 gen 26320 top level 5 path opt
ID 380 gen 27558 top level 5 path srv
ID 383 gen 27624 top level 5 path roothome
ID 384 gen 29452 top level 5 path usrlocal
ID 393 gen 30017 top level 5 path varlog34

I plan to back up:

  • root34
  • home
  • roothome
  • usrlocal
  • srv
  • opt

I plan to exclude:

  • varcache34
  • varlibmachines34
  • varlog34
  • vartmp34

Snapshots are saved to <FS_TREE>/.snapshots/. This is just a directory.

2 Likes