HOWTO - GPT/UEFI install with full disk encryption: BTRFSonLUKS with separate root, home and pkg subvolumes; hibernation with a swapfile; auto-snapshots with easy system rollback (GUI); boot into snapshots


+++ VERBOSE VERSION ++++++++++++++++++++++++++++++++

If you’re installing to a dedicated hdd/sdd just copy & paste the commands provided above!
The following information is just for the technical interested and/or in the event you have a more complicated setting (e. g. dual boot and/or already have multiple vfat or luks partitions); in which case the provided commands may mess up your existing setup and need to be adjusted according to the info below.


V-01 - Boot into Live-CD environment & set up partitions

Just follow step #01 described above or adjust to your needs.


V-02 - Change default btrfs calamares settings for fstab

There won’t be any problems just following step #02 described above.

By default the Calamares installer only uses btrfs compression (lzo method) if it detects a ssd.
One can change the used compression method(s) in the installed system itself later on, but this would only compress new files or require recompression of all old files. So why not skip these extra steps and let the installer get it right in one go without adding any extra time to the install.

The following will force calamares to assign and use compression on all btrfs installation devices; regardless of being ssd or hdd. This being 2020 with 5.x kernels we’ll use zstd compression.

You can find some information about btrfs compression here.

Edit /usr/share/calamares/modules/fstab.conf by running

sudo leafpad /usr/share/calamares/modules/fstab.conf

and change compress=lzo to compress=zstd in the ssdExtraMountOptions: [...] btrfs: section.

If you want to use compression on a hdd also, just add compress=zstd after autodefrag in the mountOptions: [...] btrfs: section. Save & exit.


V-03 - Install system with calamares (off- or online install)

Just follow step #03 described above.


V-04 - Mount newly installed btrfs system to temporary directory /mnt/btrfs

sudo mkdir /mnt/btrfs

Find your still unlocked luks device which contains the newly installed system.

sudo blkid -o device | grep luks

The command should produce something like /dev/mapper/luks-4e2dc3d4-16ce-44b9-a6be-a4a73a06e854. Your luks-… device UUID will differ though.

If the command returns more than one /dev/mapper/luks-… line you may have to run lsblk and/or sudo blkid and identify the right one.

Now mount the decrypted device to /mnt/btrfs. Be sure to use your luks-… device!

sudo mount -o subvolid=5 /dev/mapper/luks-4e2dc3d4-16ce-44b9-a6be-a4a73a06e854 /mnt/btrfs


V-05 - Adjust/repair fstab for brtfs

If the root file system is btrfs, the fsck order should be set to 0 instead of 1 or higher. The standard fsck has no effect on btrfs partitions and therefore can/should be disabled. Calamares doesn’t set this up right for btrfs partitions and subvolumes so we’ll correct this.

Edit /mnt/btrfs/@/etc/fstab by running

sudo leafpad /mnt/btrfs/@/etc/fstab

Change the last number of all lines that start with your /dev/mapper/luks-… device (the one from above) to 0. Save & exit.


V-06 - Create a top-level subvolume for the pacman cache

Calamares has automatically set up / (root) and /home as seperate top-level subvolumes. This has the advantage that although /home being mounted under /, if we snapshot the root subvolume /, the /home data and all other subvolumes won’t be stored in that snapshot. We can later restore any root snapshot without affecting the /home or other subvolume data.

On Arch Linux, a subvolume for /var/cache/pacman/pkg is often recommended to make sure the large pacman cache, that often contains GB’s of changing but easily redownloadable data, is excluded from snapshots. (Other canditates for exclusion, e. g. /var/abs, /var/tmp, /srv, aren’t part of this tutorial.)
I ran into some trouble during testing when restoring snapshots while setting this up as nested subvolumes, so I decided to stick to top-level subvolumes for this. The downside is we’ll need an entry in fstab for each top-level subvolume.

Create a top-level subvolume named @var-cache-pacman-pkg that will hold all future data stored in /var/cache/pacman/pkg.

sudo btrfs subvolume create /mnt/btrfs/@var-cache-pacman-pkg

As we have a newly installed system, /var/cache/pacman/pkg is still empty, so we don’t need to move any existing data.
We just need to add an entry to /etc/fstab to mount our newly created subvolume to /var/cache/pacman/pkg on the next system start.

  1. Edit /mnt/btrfs/@/etc/fstab with sudo leafpad /mnt/btrfs/@/etc/fstab
  2. Duplicate the line starting with /dev/mapper/luks-... that contains /home

In this newly created duplicate entry

  1. replace /home with /var/cache/pacman/pkg
  2. replace @home with @var-cache-pacman-pkg

Save & exit.

ALTERNATIVELY, you can substitute steps 1. - 4. by running the following commands

fstabstr=`grep "/home" /mnt/btrfs/@/etc/fstab`
fstabstr=${fstabstr//\/home/\/var\/cache\/pacman\/pkg}
fstabstr=${fstabstr//@home/@var-cache-pacman-pkg}
sudo sh -c "echo $fstabstr >> /mnt/btrfs/@/etc/fstab"


V-07 - Optional: Make a swap file in it’s own top-level subvolume & activate hibernation

Note: If you want to use hibernation, then you must add swap because the content of the RAM will be written to the swap partition/file. This also means that the swap size should be at least the size of RAM. Read here about choosing swap sizes.

Swap files in btrfs are supported since Linux kernel 5.x but the swap file cannot be on a snapshotted subvolume. The proper procedure is to create a new subvolume to place the swap file in.

#- Create subvolume named @swap (that will be mounted as /swap in the future)

sudo btrfs subvolume create /mnt/btrfs/@swap

#- Create mountpoint /swap

sudo mkdir /mnt/btrfs/@/swap

#- Create an entry in /etc/fstab to mount the subvolume @swap to /swap on every boot

sudo leafpad /mnt/btrfs/@/etc/fstab

Add the following line …

/dev/mapper/luks-4e2dc3d4-16ce-44b9-a6be-a4a73a06e854 /swap btrfs subvol=@swap,defaults,compress=no 0 0

Make sure to use your luks-UUID! The compress=no makes sure that that there won’t be any btrfs-compression applied to this subvolume holding our swap file. Swap files and btrfs compression don’t do well together!

Save and exit.

#- Create the actual swap file

The following will create and activate a swap file of e. g. 8GB size named swapfile in the subvolume @swap. Read here about general swap file creation. Don’t forget to adjust the size to your needs.

sudo truncate -s 0 /mnt/btrfs/@swap/swapfile
sudo chattr +C /mnt/btrfs/@swap/swapfile
sudo btrfs property set /mnt/btrfs/@swap/swapfile compression none

sudo fallocate -l 8G /mnt/btrfs/@swap/swapfile

sudo chmod 600 /mnt/btrfs/@swap/swapfile
sudo mkswap /mnt/btrfs/@swap/swapfile
sudo swapon /mnt/btrfs/@swap/swapfile

#- Edit /etc/fstab to enable the swap file after reboot

sudo leafpad /mnt/btrfs/@/etc/fstab

Add the following line …

/swap/swapfile none swap defaults 0 0

Save and exit.

#- Optional: Set up Hibernation into swap file on Btrfs

Hibernation onto a swapfile is supported by recent versions of systemd. But at time of writing, since systemd 244, systemctl hibernate does not work with a swap file on Btrfs. The systemd issue 14249 was fixed on 2020-01-08, but there is not any release since then.
See below for an alternative method of invoking hibernation!

We need to let the system know the physical offset of our newly created swap file. This wiki entry describes how to do this for btrfs.

  1. Download and compile a tool to calculate the pysical offset of the swap file …
wget https://raw.githubusercontent.com/osandov/osandov-linux/master/scripts/btrfs_map_physical.c
gcc -O2 -o btrfs_map_physical btrfs_map_physical.c

  1. Now run the tool …

sudo ./btrfs_map_physical /mnt/btrfs/@swap/swapfile

  1. Note the the first physical offset returned by this tool (the last number of the first line containing numbers). In this example, we use 5749440512.

  2. Also note the pagesize (usually 4096) that can be found with

getconf PAGESIZE

  1. To compute the resume_offset value, divide the physical offset by the pagesize. In this example, it is 5749440512 / 4096 = 1403672 .

  2. We need to add the resume_offset to /etc/default/grub

sudo leafpad /mnt/btrfs/@/etc/default/grub

Add resume_offset=1403672 (be surre to use your calculated offset!) after loglevel=3 on the line starting with GRUB_CMDLINE_LINUX_DEFAULT= . Save & exit.

  1. Configure the initramfs
    When an initramfs with the base hook is used, which is the default, the resume hook is required in /etc/mkinitcpio.conf . Whether by label or by UUID, the swap partition is referred to with a udev device node, so the resume hook must go after the udev hook.

sudo leafpad /mnt/btrfs/@/etc/mkinitcpio.conf

Add resume after filesystems on the line starting with HOOKS= . Save & exit.

You will need to let the system know that your grub and initramfs settings have changed. This is covered in the next section.


V-08 - Let the system know that some things have changed & install some needed software

#- chroot into your system

  1. make a temporary folder and mount your root system and efi folder

sudo mkdir /mnt/chroot

Mount the root system to the newly created folder. Use your luks-… device (see V-04).

sudo mount -o compress=zstd,subvol=@ /dev/mapper/luks-4e2dc3d4-16ce-44b9-a6be-a4a73a06e854 /mnt/chroot

Mount the partition containing your EFI data (e. g. /dev/sda1) to /mnt/chroot/boot/efi

sudo mount /dev/sda1 /mnt/chroot/boot/efi

  1. now chroot into the mounted system

sudo arch-chroot /mnt/chroot

#- While we’re in the chroot environment we’ll just install some software we’ll need later on to our newly installed system.

sudo pacman --noconfirm -S cronie
sudo systemctl enable cronie.service

#- Now we’ll update Grub and generate updated kernel images.

sudo grub-mkconfig -o /boot/grub/grub.cfg
sudo mkinitcpio -p linux

V-09 - REBOOT into your new system & run the following …

sudo pacman --noconfirm -Syy &&
yay --noconfirm -S timeshift timeshift-autosnap grub-btrfs

This will install the following:

  • timeshift => A system restore utility for Linux
  • timeshift-autosnap => Timeshift auto-snapshot script which runs before package upgrade using Pacman hook.
  • grub-btrfs => Include btrfs snapshots in GRUB boot options

V-10 - Configure Timeshift (/etc/timeshift.json)

  1. Open Timeshift from the menu
  2. Select “BTRFS” as the “Snapshot Type”; continue with “Next”
  3. Choose your btrfs system partition as “Snapshot Location”; continue with “Next”
  4. “Select Snapshot Levels” (type and number of snaphots that will be automatically created and managed/deleted by Timeshift)
    Recommendation:
  • Keep “Daily” at 5
  • Activate “Boot”, but change to 3; continue with “Finish”
  1. Optional: “Create” a manual first snapshot & exit Timeshift

Timeshift will now check on every full hour if snapshots (hourly, daily, weekly, monthly) need to be created or deleted. Note that boot snapshots will actually be created about 10 minutes after boot, not directly at system startup!


V-11 - Configure timeshift-autosnap (/etc/timeshift-autosnap.conf)

Timeshift-autosnap creates timeshift snapshots whenever it detects a pacman (and yay invoked) upgrade. This way you should be able to rollback to a state right before the package upgrades.

By default only the last 3 snapshots are kept. I recommend leaving it at that value.


V-12 - Configure grub-btrfs (/etc/default/grub-btrfs/config)

Grub-btrfs creates Grub boot entries for your snapshots. I recommend leaving the configuration as is.

Grub-btrfs should be able to monitor the snapshot folder for changes and automatically update grub if it detects such a change. I wasn’t able to get this functionality to work reliably and therefore decided to just update the grub boot entries on every system shutdown (see V-13).


V-13 - Limit the maximum amount of snapshots & update grub boot entries

Timeshift cleans the automatically created snapshots and timeshift-autosnap has a default limit of 3 snapshots after upgrades, BUT the ones you create manually aren’t handled by Timeshift and would be kept indefinetily. High amounts of old snapshots could potentially cause slowdowns, so we’re going to limit the amount of maximum snapshots your system will allow and keep.

For this we will create a systemd service that runs right before you shutdown or restart the system and

  • keeps only the newest (e. g. 13) snapshots and deletes all the older ones
  • updates grub to contain only these snapshots as boot entries

I recommend to limit the amount of snapshots to about 13 to 15.

Create a systemd service called “limit-timeshift-snapshots-update-grub”:

sudo leafpad /etc/systemd/system/limit-timeshift-snapshots-update-grub.service

Now add the following content (you can adjust the number of snapshots to keep by changing the number from 13 to whatever in the ExecStop=... line; just choose a high number if you prefer to delete these manually):

[Unit]
Description=Keep only latest 13 timeshift snapshots and update grub.cfg before shutdown
Before=poweroff.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/true
ExecStop=/usr/bin/sh -c "cd /run/timeshift/backup/timeshift-btrfs/snapshots/ && rm -rf `ls -1r | awk 'NR>13'` && /usr/bin/grub-mkconfig -o /boot/grub/grub.cfg"


[Install]
WantedBy=multi-user.target

Save & exit.

Next we’ll enable and start the newly created service.

sudo systemctl enable limit-timeshift-snapshots-update-grub.service &&
sudo systemctl start limit-timeshift-snapshots-update-grub.service

V-14 - Activate/Invoke Hibernation

The normal way of invoking hibernation would be to run/call systemctl hibernate . But, as stated above, there seems to be a bug in the current systemd that causes this to fail.

e. g. Failed to hibernate system via logind: Not enough swap space for hibernation

Until this is fixed, here is a workaround; activate hibernation by running:

sudo sh -c 'echo disk > /sys/power/state'


V-15 - Restore a snapshot (rollback the system)

See the # - How to RESTORE a snapshot (previous system state) section in the previous post.


8 Likes