sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
zram0 252:0 0 8G 0 disk [SWAP]
nvme1n1 259:0 0 953.9G 0 disk
├─nvme1n1p1 259:1 0 190M 0 part /boot/efi
├─nvme1n1p3 259:3 0 1G 0 part /boot
└─nvme1n1p7 259:7 0 932.6G 0 part
└─luks-13f88890-0b52-4080-a3b6-b406d616c659 253:0 0 932.6G 0 crypt /home
nvme2n1 259:8 0 931.5G 0 disk
nvme0n1 259:10 0 476.9G 0 disk
└─nvme0n1p1 259:11 0 476.9G 0 part
Duplicate the /boot and /boot/efi partition structure of disk one. sudo parted /dev/nvme2n1
Use Disks
to create the btrfs with LUKS
sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
zram0 252:0 0 8G 0 disk [SWAP]
nvme1n1 259:0 0 953.9G 0 disk
├─nvme1n1p1 259:1 0 190M 0 part /boot/efi
├─nvme1n1p2 259:3 0 1G 0 part /boot
└─nvme1n1p3 259:7 0 932.6G 0 part
└─luks-13f88890-0b52-4080-a3b6-b406d616c659 253:0 0 932.6G 0 crypt /var/lib/docker/btrfs
nvme2n1 259:8 0 931.5G 0 disk
├─nvme2n1p1 259:1 0 190M 0 part
├─nvme2n1p2 259:3 0 1G 0 part
└─nvme2n1p3 259:9 0 931.5G 0 part
└─luks-429c570c-2743-4cc3-beaa-dfc8facb118c 253:1 0 931.5G 0 crypt
nvme0n1 259:10 0 476.9G 0 disk
└─nvme0n1p1 259:11 0 476.9G 0 part
sudo btrfs device usage /
/dev/mapper/luks-13f88890-0b52-4080-a3b6-b406d616c659, ID: 1
Device size: 932.57GiB
Device slack: 0.00B
Data,single: 85.01GiB
Metadata,single: 8.01GiB
System,single: 4.00MiB
Unallocated: 839.55GiB
We will have to add the -f
option to force replacement of the filesystem on the disk if it has an existing filesystem. This is destructive!
I also takes a long time if there is a lot of data. Be patient. You can follow progress by running the usage command from a different terminal.
sudo btrfs device add -f /dev/mapper/luks-429c570c-2743-4cc3-beaa-dfc8facb118c /
sudo btrfs device usage /
sudo btrfs balance start -dconvert=raid1 -mconvert=raid1 /
sudo btrfs device usage /
Add to /etc/crypttab:
luks-429c570c-2743-4cc3-beaa-dfc8facb118c UUID=429c570c-2743-4cc3-beaa-dfc8facb118c none discard
Now we need to add the luks to grub.
Add rd.luks.uuid=luks-429c570c-2743-4cc3-beaa-dfc8facb118c
to the GRUB_CMDLINE_LINUX value in /etc/default/grub
Then rebuild grub.
Then rebuild initramfs.
sudo -i
nano /etc/default/grub
grub2-mkconfig -o "$(readlink /etc/grub2.cfg)"
dracut --force
reboot
sudo dd if=/dev/nvme1n1p2 of=/dev/nvme2n1p2 bs=1024 status=progress
sudo dd if=/dev/nvme1n1p1 of=/dev/nvme2n1p1 bs=1024 status=progress
Snapper is a snapshot management tool for btrfs. It is available here. http://snapper.io/
If you are using Docker's btrfs volume driver you end up with a lot snapshots which necessitates the grep filter used below.
sudo btrfs subvolume list / | grep "level 5"
ID 256 gen 1987943 top level 5 path home
ID 257 gen 1987942 top level 5 path root
This installs snapper and an automatic snapshot before and after each dnf install.
sudo dnf install snapper python-dnf-plugin-snapper
Here we create a snapper config for the root
subvolume.
sudo snapper -c root create-config /
It is not stored at the top level however, so we will move the subvolume from /.snapshots
(within the root
subvolume) to level 5
. We want it at the top level so we can rollback our root partition and boot into it directly.
sudo btrfs subvolume list / | grep snapshots
sudo btrfs subvolume delete /.snapshots
Now we will make a new folder within the root path and create a top level subvolume and mount it there.
sudo mkdir /.snapshots
sudo mkdir /backups
sudo mkdir /mnt/btrfs
sudo mount /dev/dm-0 -o subvolid=5 /mnt/btrfs
cd /mnt/btrfs
sudo btrfs subvolume create snapshots
sudo btrfs subvolume create backups
cd ..
sudo umount /mnt/btrfs
sudo rmdir btrfs/
There are now two new top level subvolume.
sudo btrfs subvolume list / | grep "level 5"
ID 256 gen 1987943 top level 5 path home
ID 257 gen 1987942 top level 5 path root
ID 10963 gen 1987638 top level 5 path snapshots
ID 10963 gen 1987638 top level 5 path backups
sudo nano /etc/fstab
We add a new lines at the bottom which mounts the new subvolumes.
UUID=2402f445-7fd2-4a8b-8b53-6c27c67fb58d / btrfs subvol=root,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=13f88890-0b52-4080-a3b6-b406d616c659 /boot ext4 defaults 1 2
UUID=62A1-49D9 /boot/efi vfat umask=0077,shortname=winnt 0 2
UUID=2402f445-7fd2-4a8b-8b53-6c27c67fb58d /home btrfs subvol=home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=2402f445-7fd2-4a8b-8b53-6c27c67fb58d /.snapshots btrfs subvol=snapshots,x-systemd.device-timeout=0 0 0
UUID=2402f445-7fd2-4a8b-8b53-6c27c67fb58d /backups btrfs subvol=backups,x-systemd.device-timeout=0 0 0
Let's mount it.
sudo mount -a
We will make user folder with user permissions.
sudo mkdir /backups/$USER
sudo chown $USER:$USER /backups/$USER
cd $HOME
ln -s /backups/$HOME backups
The default subvolume is just top level. This is not specific enough for our needs. We want to be able to set this explicitly to a subvolume ID.
sudo btrfs subvolume get-default /
ID 5 (FS_TREE)
Recall above our root
subvolume ID was 257
we will set that to our default.
sudo btrfs subvolume set-default 257 /
sudo btrfs subvolume get-default /
ID 257 gen 1988138 top level 5 path root
Now we need to modify the Grub
config.
sudo grubby --info=ALL
...
index=2
kernel="/boot/vmlinuz-5.13.14-200.fc34.x86_64"
args="ro rootflags=subvol=root rhgb quiet"
...
We want to remove the rootflags=subvol=root
argument so it will honor rollbacks requested by snapper.
sudo grubby --update-kernel=ALL --remove-args="rootflags=subvol=root"
sudo grubby --info=ALL
...
index=2
kernel="/boot/vmlinuz-5.13.14-200.fc34.x86_64"
args="ro rhgb quiet"
...
All better, so now we reboot.
reboot
sudo snapper ls
# | Type | Pre # | Date | User | Cleanup | | Userdata
---+--------+-------+-----------------+------+---------+----------------------------------------+---------
0 | single | | | root | | current |
1 | pre | | Sun 18 Sep 2021 | root | number | /bin/dnf -y install ...fc34.x86_64.rpm |
2 | post | 1 | Sun 19 Sep 2021 | root | number | /bin/dnf -y install ...fc34.x86_64.rpm |
Oh no, our install broke everything!
sudo snapper --ambit classic rollback 1
reboot
Life is good!
We will create a snapper config for /home and give our personal user access since this is a single user PC.
sudo snapper -c home create-config /home
sudo snapper -c home set-config SYNC_ACL=yes ALLOW_USERS=$USER
sudo snapper list-configs
Config | Subvolume
-------+----------
home | /home
root | /
Notice sudo
is not required anymore.
snapper -c home create --description "First Snapshot"
snapper -c home ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+-----------------+------+----------+----------------+---------
0 | single | | | root | | current |
1 | single | | Sat 18 Sep 2021 | josh | | First Snapshot |
sudo nano /etc/snapper/configs/root
Disable automatic hourly snapshots of root
.
# create hourly snapshots
TIMELINE_CREATE="no"
# cleanup hourly snapshots after some time
TIMELINE_CLEANUP="not"
sudo nano /etc/snapper/configs/home
Configure automatic hourly snapshots of home
.
# create hourly snapshots
TIMELINE_CREATE="yes"
# cleanup hourly snapshots after some time
TIMELINE_CLEANUP="yes"
# limits for timeline cleanup
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="1"
TIMELINE_LIMIT_YEARLY="0"
Turn on timer for automatic snapshots.
sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer
After waiting an hour...
snapper -c home ls
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+-----------------+------+----------+----------------+---------
0 | single | | | root | | current |
1 | single | | Sat 18 Sep 2021 | josh | | First Snapshot |
2 | single | | Sun 18 Sep 2021 | root | timeline | timeline |
Thank you for the excellent walkthrough. I would appreciate clarification on a few items:
Ambit classic, why was this a chosen option?
The man page states:
What is the backups directory used for? It doesn't seem to be a function of snapper that I can tell.