Installing Debian on btrfs subvolume

When installing Debian on my new laptop I decided that I'll give the Btrfs a try. I wanted to have a possibility to create snapshots easily and to be able to rollback the system when needed. The steps described here should work on most linux distributions with reasonably up-to-date kernel and userland.

Debian Installation

I installed Debian as usual. I did no special setup during the Debian installation. All btrfs related setup was done after the system was installed.
I didn't bother with creating separate partition for /home because I wanted to have /home on btrfs subvolume as well.

Setting up btrfs subvolumes

When finished installation, I booted SystemRescueCD from prepared USB stick.

There I mounted the Debian filesystem:

# mkdir /mnt/rootfs/
# mount /dev/sda3 /mnt/rootfs/

Then I created the subvolume "@". I followed the Ubuntu-specific subvolume layout as described in https://help.ubuntu.com/community/btrfs.

# btrfs subvol create /mnt/rootfs/@

To check that the subvolume was created:

# btrfs subvolume list /mnt/rootfs/
ID 268 gen 4624 top level 5 path @

Then I moved all data to the newly created subvolume:

# cd /mnt/rootfs
# ls -1 | egrep -v '@|home' | while read d; do mv "$d" @/; done

I created subvolume for /home in the same way:

# btrfs subvol create /mnt/rootfs/@home

Then moved /home to subvolume and created the mountpoint:

# mv home/* @home/
# rmdir home
# mkdir @/home

Then checked that all data are in the corresponding subvolumes:

# ls -l /mnt/rootfs/
total 0
drwxr-xr-x 1 root root 204 Jul 16 02:26 @
drwxr-xr-x 1 root root 14 Jul 16 18:51 @home

and

# btrfs subvolume list /mnt/rootfs/
ID 268 gen 4624 top level 5 path @
ID 426 gen 5355 top level 5 path @home

Next it was necessary to alter the /etc/fstab (in /mnt/rootfs/@/etc/fstab) and add /home mountpoint configuration there. Mine looks like this:

/dev/sda3  /home  btrfs  noatime,subvol=@home  0  0

In order to be able to boot into Debian, it was necessary to set the "@" subvolume as default temporarily:

# btrfs subvolume set-default 268 /mnt/rootfs/

The number "268" is ID of the "@" subvolume, see the subvolume list output above.

Now it's time to reboot back to Debian.

Grub2 settings

Everything went fine, I'm back in Debian.

Now I need to change the default subvolume back to the top level volume. Otherwise the grub setup would break after the next "update-grub".
The top level subvolume has always the ID 5. First I need to mount it:

# mkdir /mnt/rootfs/
# mount -o subvolid=5 /dev/sda3 /mnt/rootfs/

Now I can change the default subvolume:

# btrfs subvolume set-default 5 /mnt/rootfs/

Now it's possible to update grub configuration. The "update-grub" script correctly detects that the system is using the "@" btrfs subvolume and updates the /boot/grub/grub.cfg config accordingly. That's the reason why it's not possible to continue using the "@" subvolume as default subvolume (with "set-default 268") instead of the top level volume. The /boot/grub/grub.cfg would have configuration like "linux /@/boot/vmlinuz" but there is no other "@" subvolume in the "@" subvolume and thus the path would be incorrect.

Therefore I need to update grub configuration:

# update-grub

Check that the /boot/grub/grub.cfg was updated:

# grep '/@/' /boot/grub/grub.cfg
if loadfont /@/usr/share/grub/unicode.pf2 ; then
  set locale_dir=($root)/@/boot/grub/locale
if background_image /@/usr/share/images/desktop-base/joy-grub.png; then
	linux	/@/boot/vmlinuz-3.9-1-amd64 root=UUID=4577942d-0841-42e9-a2f3-3d04b91ff2d5 ro rootflags=subvol=@  i915.i915_enable_rc6=1 i915.lvds_downclock=1 pcie_aspm=force cgroup_enable=memory quiet
	initrd	/@/boot/initrd.img-3.9-1-amd64
	linux	/@/boot/vmlinuz-3.9-1-amd64 root=UUID=4577942d-0841-42e9-a2f3-3d04b91ff2d5 ro single rootflags=subvol=@ 
	initrd	/@/boot/initrd.img-3.9-1-amd64

Now I need to reinstall grub with the changed configuration, otherwise it would not be able to find the necessary files on new locations:
# grub-install /dev/sda

Final touch

The only change left is altering the "/" entry in /etc/fstab to include the "subvol=@" mount option. Mine looks like this:

UUID=4577942d-0841-42e9-a2f3-3d04b91ff2d5  /  btrfs  noatime,subvol=@,compress=lzo  0  0

Reboot and done.

Rollbacks

Doing snapshots and rollbacks is now super easy:

# mount /dev/sda3 /mnt/rootfs/
# cd /mnt/rootfs/
# btrfs subvolume snapshot @ @_snapshot

If I later decide that I want to boot from the "@_snapshot" instead of "@", I just need to rename the subvolumes:

# cd /mnt/rootfs/
# mv @ @_old
# mv @_snapshot @

Reboot and I'm using the newly created subvolume (snapshot). If I don't need the "@_old" subvolume anymore, I can just delete it:

# mount /dev/sda3 /mnt/rootfs/
# cd /mnt/rootfs/
# btrfs subvolume delete @_old

Feedback

Feel free to comment on the article, my English and/or share the article on social networks.

Tags: 

20 Comments

That's how it should be done.

Very nice tutorial, that's how it should be done, thanks!

The only minuscule thing I noticed is that you have 2 redundant mount options:

  • "defaults" is unnecessary, since the options that it sets is the default starting point anyway even if you don't explicitly say "defaults". It's only useful when you would otherwise specify no mount options at all in /etc/fstab, since it needs a non-empty field either way.
  • "nodiratime" is unnecessary when already specifying "noatime", since "noatime" prevents all atime updates anyway.

Debian 7.2

Hello, thanks for your tutorial:

I had problems in the final steps, as setting the default-id to 5 won't work - update-grub won't add the /@ to paths and the system will fail to load.

I've decided to set default id to @:
btrfs subvolume default-id @ /mnt/rootfs

and to leave out the subvol=@ option in fstab.

should i need to boot into a previous version of root i can use grub parameters:
'rootflags=subvol=[subvolume_id]'

I came to this conclusion with the helpful info given by 'patriku' on debian irc channel.

Never the less I thought I should post my issues here and ask you: Could i be missing any snapshot functionality with the solution I came up with?

Thank you.

RE: Debian 7.2

Hi Bruno,

you said that setting the default-id to 5 won't work for you. How does it fail? Can you do

# mount -o subvolid=5 /dev/sdaXXX /mnt/rootfs/

?
If you can, what is the output of

# btrfs subvolume list /mnt/rootfs/

?

Top level subvolume should always have the ID 5 so

# btrfs subvolume set-default 5 /mnt/rootfs/

should work.

I guess that your setup should work as well but I'm not sure how the grub-update detects the subvolumes and if your grub.cfg will not break during future grub-update.

My setup is basically the same as what the Ubuntu installer produces so it should be proven and tested to work well.

This process might run out of

This process might run out of disk space if /home is too big. Also mv across subvolumes is slow and cpu intensive with large directories. Therefore it would be better to create a snapshot: "btrfs subvolume snapshot /mnt/rootfs /mnt/rootfs/@". Repeat for /home. You won't need to set the default subvolume now since the grub files are still in the btrfs root, just append rootflags=subvol=@ when you reboot, run grub-install, mount -osubvolid=5 to access the root, and rm the old directories (including /boot) in the subvolume root.

Damn, you ninja'd me: just

Damn, you ninja'd me: just this week I tried the same after I discovered that you can snapshot the root subvol. You can therefore (my btrfs root is mounted on /mnt):

btrfs subvol snapshot /mnt/ /mnt/@
for i in /dev /dev/pts /proc /sys /run; do sudo mount --bind $i /mnt$i; done
vi /btrfs-root/etc/fstab
# add something like: /dev/sdX1  /  btrfs  noatime,subvol=@  0   0
chroot /mnt/@
mount /
update-grub
grub-install /dev/sdX
exit

(from the top of my head, typos reserved); no need for a rescue disk or set-default.

But, before I seem unthankful...

...Martin did put me on the right track with this post. Now let's hope the Debian installer soon supports this out of the box.

Next time

Thanks guys, I'll try your approach next time I'll install Debian.

So Awesome...

i've read through this page several times, with a fresh debian btrfs install trying to work out what was going on, and farting around with usb sticks.

I then read it through one more time to let it sink in, and realised this was not only an excellent strategy for achieving what i wanted to do, but a stunning example of beautiful hacking.

martin, chris, roel - respect to the three of you!

mount btrfs

does not work mount /dev/sda3 /mnt/rootfs/
but so works mount -o subvolid=5 /dev/sda3 /mnt/rootfs/

RE: mount btrfs

It does work if you run

# btrfs subvolume set-default 5 /mnt/rootfs/

after

# mount -o subvolid=5 /dev/sda3 /mnt/rootfs/

See "Grub2 settings" section.

ls -A

ls -1 | egrep -v '@|home' | while read d; do mv "$d" @/; done

should be

GRUB_DEVICE_UUID=""

update-grub was throwing these errors:

/usr/sbin/grub-probe: error: cannot find a GRUB drive for /dev/vda5

I googled it and came up with this:
http://wiki.gentoo.org/wiki/Btrfs_native_system_root#Running_grub2_mkconfig
and this:
https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/450260

So I appended /etc/defaults/grub with this:
GRUB_DEVICE_UUID="b1450df5309d498da9f64690b6c0e7f7"

...and so update-grub succeeded.

GRUB_DEVICE=/dev/sda5

actually, my last post was wrong (on todays date), it wont work unless you call out the device name. This must be a bug because using device names like this has been depreciated all over the linux world in favor of UUID:

GRUB_DEVICE=/dev/sda5

How to reinstall grub?

I tried this in a VM and it worked great.

Then I tried to do a grub reinstall from systemrescuecd (because I always end up needing to do this at least once in the life of any machine), and ran into an error on 'grub-install /dev/sda' (I skipped update-grub because grub.cfg was already correct).

Path '/boot/grub' is not readable by GRUB on boot. Installation is impossible. Aborting.

Anyone know a trick to be able to reinstall grub on a machine with a btrfs root subvolume? I'd still like to do this, but right now this is scaring me off.

Procedure I followed for mounting in the rescue cd is here: http://edoceo.com/notabene/grub-probe-error-cannot-find-device-for-root.

Also, if I _do_ try to run update-grub first, I get the error mentioned on that page, "cannot find a device for / (is /dev mounted?)". I even tried replacing grub-probe with a script as mentioned on that page, but then just ended up with the grub-install error above.

RE: How to reinstall grub?

Do you mount '/' with 'subvol' (or 'subvolid'), or do you mount default subvolume?

These step won't work:

These step won't work:
# btrfs subvol create /mnt/rootfs/@

Get Message: ERROR: cannot create subvolume - Inapropriate ioctl for device

Any Idea?

Hi, there everything works

Hi, there everything works fine, so far after i reboot to debian, get grub rescue....

btrfs boot debian8 from subvolume

Hi folks, still not able to boot from subvolume. always grub rescue or when boot then still from "/" and not from @root/

No need for SystemRescueCD

Hi,

Thanks for great instructions. Though I was too lazy though to boot from SystemRescueCD so I skipped that part. And because default installation of Debian seems to mount the filesystem to / by default there is no need for mounting it either.

I just created a snapshot of rootfs (named @), and new subvolume for home and moved files from @/home to @home. Then after I got the system booting properly from the snapshot, I just removed the original files from the filesystem root.

I have /boot on separate partition so I don't have those /@/boot/... paths in grub.cfg.

Also I'm using RAID1 setup, which didn't cause any issues if someone is interested. Only extra work is to install grub to both disks, to make sure system can boot if another disk fails.

Subscribe to Comments for "Installing Debian on btrfs subvolume"