Fedora Workstation 33: installation guide with btrfs-luks full disk encryption (optionally including /boot) and auto snapshots with Timeshift
Please feel free to raise any comments or issues on the website’s Github repository. Pull requests are very much appreciated.
Table of Contents
Overview
Since Fedora switched their default filesystem to btrfs I decided to give it a go as I am exclusively using btrfs on all my systems, see: Why I (still) like btrfs. Fedora’s automatic installation routine with encryption is actually almost perfect for me except some changes regarding the btrfs mount options and subvolume names.
So, in this guide I will show how to install Fedora 33 with the following structure:
- a btrfs-inside-luks partition for the root filesystem containing a subvolume
@
for/
and a subvolume@home
for/home
. Note that the Fedora installer creates subvolumesroot
for/
andhome
for/home
, so we need to rename these otherwise Timeshift will not work properly. - an unencrypted EFI partition for the GRUB bootloader
- Optionally, I will also show how to encrypt the
/boot
partition, so you get a full-disk-encryption-system (including/boot
) and only one passphrase prompt from GRUB. - there is no need for a swap partition as Fedora creates a SwapOnZram during start-up
- automatic system snapshots and easy rollback similar to zsys using:
- Timeshift which will regularly take (almost instant) snapshots of the system
- timeshift-autosnap-dnf which will automatically run Timeshift on any dnf operation and also keep a backup of your EFI partition inside the snapshot (NOT YET)
- grub-btrfs which will automatically create GRUB entries for all your btrfs snapshots
With this setup you basically get the same comfort of Ubuntu’s 20.04’s ZFS and zsys initiative, but with much more flexibility and comfort due to the awesome Timeshift program, which saved my bacon quite a few times. This setup works similarly well on other distributions, for which I also have installation guides with optional RAID1.
If you ever need to rollback your system, checkout Recovery and system rollback with Timeshift.
Step 0: General remarks
I strongly advise to try the following installation steps in a virtual machine first before doing anything like that on real hardware!
So, let’s spin up a virtual machine with 4 cores, 8 GB RAM, and a 64GB disk using e.g. my fork of the awesome bash script quickemu. I can confirm that the installation works equally well on my Dell XPS 13 9360 and my Dell Precision 7520.
This tutorial is made with Fedora 33 Workstation from https://getfedora.org/de/workstation/download/ copied to an installation media (usually a USB Flash device but may be a DVD or the ISO file attached to a virtual machine hypervisor).
Step 1: Graphical installer with automatic configuration and encryption
Boot the installation medium in UEFI mode and choose Install to Harddrive
. Choose your language, Keyboard, Time & Date setting. Note that the Done button is (weirdly) on the top left corner. Then hit Installation Destination
. Choose your harddisk and
- select
Automatic
underStorage Configuration
Encrypt my data
underEncryption
Click Done
and enter your disk encryption passphrase, choose a good one. You will return to the Installation Summary screen. Click Begin Installation
. When the installation process is finished, select Finish Installation
. Reboot your system and go through the welcome screen.
Step 2: Post-Installation steps
Open a terminal (META + term + Enter), switch to an interactive root session and run the following commands:
sudo -i
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# zram0 251:0 0 1.9G 0 disk [SWAP]
# vda 252:0 0 64G 0 disk
# ├─vda1 252:1 0 600M 0 part /mnt/sysroot/boot/efi
# ├─vda2 252:2 0 1G 0 part /mnt/sysroot/boot
# └─vda3 252:3 0 62.4G 0 part
# └─luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf 253:2 0 62.4G 0 crypt /home
btrfs subvolume list /
# ID 256 gen 169 top level 5 path home
# ID 258 gen 169 top level 5 path root
# ID 265 gen 24 top level 258 path var/lib/machines
ls /dev/mapper
# control luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf
blkid
# /dev/vda1: UUID="4FD9-A843" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="9b25248e-f8fb-456d-8a9b-4b7025107794"
# /dev/vda2: UUID="7d52fc6e-8804-4a34-8d16-b185d6d62319" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="f4479656-e55d-46a6-bd60-0f04f833d754"
# /dev/vda3: UUID="6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf" TYPE="crypto_LUKS" PARTUUID="d20fbfac-af98-43bc-a01b-007719a37100"
# /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf: LABEL="fedora_localhost-live" UUID="11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a" UUID_SUB="7353fe42-342b-42dd-b4b0-ab00cebfacbd" BLOCK_SIZE="4096" TYPE="btrfs"
# /dev/zram0: UUID="07753a1d-ee0b-49b3-a6fc-0c1828bf1727" TYPE="swap"
Note that my disk is called vda
, in most cases they are called sda
for normal SSD and HDD, whereas for NVME storage the naming is nvme0
. Also my luks crypt is named luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf
, where the number is the UUID of /dev/vda3
.
Mount the btrfs top-level root filesystem
Let’s mount our the top-level btrfs volume (which has always id 5):
mkdir /btrfs_pool
mount -o subvolid=5 /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf /btrfs_pool
ls /btrfs_pool
# home root
Create btrfs subvolumes @
and @home
Now the simplest way to get @
and @home
is to rename root
and home
, i.e.:
mv /btrfs_pool/root /btrfs_pool/@
mv /btrfs_pool/home /btrfs_pool/@home
btrfs subvolume list /btrfs_pool
# ID 256 gen 345 top level 5 path @home
# ID 258 gen 346 top level 5 path @
# ID 265 gen 24 top level 258 path var/lib/machines
Make changes to fstab with optimized mount options
We need to let fstab know that our subvolume names have changed to @ and @home. Also I like to mount /btrfs_pool for better access of my subvolumes. Lastly, there is some general agreement to use the following mount options to optimize performance and durability on SSD or NVME drives:
ssd
: use SSD specific options for optimal use on SSD and NVMEnoatime
: prevent frequent disk writes by instructing the Linux kernel not to store the last access time of files and foldersspace_cache
: allows btrfs to store free space cache on the disk to make caching of a block group much quickercommit=120
: time interval in which data is written to the filesystem (value of 120 is taken from Manjaro’s minimal iso)compress=zstd
: allows to specify the compression algorithm which we want to use. btrfs provides lzo, zstd and zlib compression algorithms. Based on some Phoronix test cases, zstd seems to be the better performing candidatediscard=async
: Btrfs Async Discard Support Looks To Be Ready For Linux 5.6
Let’s make these changes with a text editor, e.g.:
nano /etc/fstab
or use these sed
commands
sed -i 's/subvol=root/subvol=@,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async/' /etc/fstab
sed -i 's/subvol=home/subvol=@home,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async/' /etc/fstab
echo "UUID=$(blkid -s UUID -o value /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf) /btrfs_pool btrfs subvolid=5,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0" >> /etc/fstab
Either way your fstab
should look like this:
cat /etc/fstab
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a / btrfs subvol=@,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=7d52fc6e-8804-4a34-8d16-b185d6d62319 /boot ext4 defaults 1 2
# UUID=4FD9-A843 /boot/efi vfat umask=0077,shortname=winnt 0 2
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /home btrfs subvol=@home,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /btrfs_pool btrfs subvolid=5,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
We need to let GRUB know about the changes as well:
grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
Step 3: Reboot, some checks, and update system
Cross your fingers and reboot:
reboot now
Now let’s open up a terminal to see whether everything is set up correctly:
sudo cat /etc/crypttab
# luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf UUID=6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf none discard
sudo cat /etc/fstab
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a / btrfs subvol=@,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=7d52fc6e-8804-4a34-8d16-b185d6d62319 /boot ext4 defaults 1 2
# UUID=4FD9-A843 /boot/efi vfat umask=0077,shortname=winnt 0 2
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /home btrfs subvol=@home,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /btrfs_pool btrfs subvolid=5,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
sudo mount -av
# / : ignored
# /boot : already mounted
# /boot/efi : already mounted
# /home : already mounted
# /btrfs_pool : already mounted
sudo mount -v | grep /dev/mapper
# /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf on / type btrfs (rw,noatime,seclabel,compress=zstd:3,ssd,discard=async,space_cache,commit=120,subvolid=258,subvol=/@)
# /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf on /btrfs_pool type btrfs (rw,noatime,seclabel,compress=zstd:3,ssd,discard=async,space_cache,commit=120,subvolid=5,subvol=/)
# /dev/mapper/luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf on /home type btrfs (rw,noatime,seclabel,compress=zstd:3,ssd,discard=async,space_cache,commit=120,subvolid=256,subvol=/@home)
sudo swapon
# NAME TYPE SIZE USED PRIO
# /dev/zram0 partition 1.9G 0B 100
sudo btrfs subvolume list /
# ID 256 gen 464 top level 5 path @home
# ID 258 gen 464 top level 5 path @
# ID 265 gen 428 top level 258 path var/lib/machines
Let’s update the system and reboot one more time:
sudo dnf upgrade
sudo reboot now
Step 4: Install Timeshift
Open a terminal and install some dependencies:
sudo dnf install -y git make
Install Timeshift and configure it directly via the GUI:
sudo dnf install timeshift
sudo timeshift-gtk
- Select btrfs as the “Snapshot Type”; continue with “Next”
- Choose your btrfs system partition as “Snapshot Location”; continue with “Next”
- “Select Snapshot Levels” (type and number of snapshots that will be automatically created and managed/deleted by Timeshift), my recommendations:
- Activate “Monthly” and set it to 2
- Activate “Weekly” and set it to 3
- Activate “Daily” and set it to 5
- Deactivate “Hourly”
- Activate “Boot” and set it to 3
- Activate “Stop cron emails for scheduled tasks”
- continue with “Next”
- I also include the
@home
subvolume (which is not selected by default). Note that when you restore a snapshot, Timeshift will let you choose whether you want to restore@home
as well (which in most cases you don’t want to). But I find it most handy to have snapshots of both at hand. - Click “Finish”
- “Create” a manual first snapshot & exit Timeshift
Timeshift will now check every hour if snapshots (“hourly”, “daily”, “weekly”, “monthly”, “boot”) need to be created or deleted. Note that “boot” snapshots will not be created directly but about 10 minutes after a system startup.
Timeshift puts all snapshots into /run/timeshift/backup
. Conveniently, the real root (subvolid 5) of your btrfs partition is also mounted here every time you start Timeshift, so it is easy to view, create, delete and move around snapshots manually. But you can also use /btrfs_pool
for that.
ls /run/timeshift/backup
# @ @home timeshift-btrfs
ls /btrfs_pool
# @ @home timeshift-btrfs
Note that /run/timeshift/backup/@
contains your /
folder and /run/timeshift/backup/@home
contains your /home
folder.
I still need to figure out similar features to timeshift-autosnap-apt and grub-btrfs. Anyways, before I do any dnf operations, I make a snapshot on the command line using:
sudo timeshift --create --comments "before upgrade"
sudo dnf upgrade
Make sure to delete the snapshots every once in a while, you could set up a systemd process for this as described here.
STEP 5 (OPTIONAL): Full disk encryption including /boot
I actually like to use a Yubikey as a second factor to unlock my luks partition, for this I need an unencrypted /boot
partition (see my Things to do after install Fedora 33), so I usually skip this step.
If your needs are different, then it is also possible to fully encrypt your system based upon security_modules in Grub2, i.e. to also put /boot
inside a luks container and let GRUB unlock this first and pass on the passphrase to unlock your root partition as well. If you want this, then follow on and open up an interactive root shell:
sudo -i
Backup the files in your boot partition
The boot partition is on /dev/vda2
, whereas the efi partition is on /dev/vda1
. Let’s backup the boot partition:
umount /boot/efi
rsync -avuP /boot/ /boot.bak/
Create luks1 partition for boot
GRUB is able to decrypt luks version 1 partitions at boot time, but by default version 2 is used in cryptsetup. So we need to prepare the luks1 partition or else GRUB will not be able to unlock the encrypted device. Note that most Linux distributions also default to version 1 if you do a full disk encryption (e.g. Manjaro Architect). This step is still work-in-progress as I need to figure out how to prompt only once for the passphrase.
umount /boot
cryptsetup luksFormat --type=luks1 /dev/vda2
# WARNING!
# ========
# This will overwrite data on /dev/vda2 irrevocably.
# Are you sure? (Type uppercase yes): YES
# Enter passphrase for /dev/vda2:
# Verify passphrase:
Now map the encrypted partition to a device called crypt-boot
and choose a filesystem (e.g. ext4 or btrfs)
cryptsetup luksOpen /dev/vda2 crypt-boot
# Enter passphrase for /dev/vda2:
ls /dev/mapper/
# control crypt-boot luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf
mkfs.btrfs /dev/mapper/crypt-boot
Correct fstab, crypttab and restore boot backup files
Let’s find out the new UUID of our encrypted boot partition:
blkid | grep crypt-boot
# dev/mapper/crypt-boot: UUID="3f75f101-bbe7-4850-9b33-a2196807fb01" UUID_SUB="984444d1-66fb-4d09-9586-8cb876653c28" BLOCK_SIZE="4096" TYPE="btrfs"
and replace the old UUID of the boot partition with the new one in /etc/fstab, also making sure that btrfs is the filesystem:
nano /etc/fstab
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a / btrfs subvol=@,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=3f75f101-bbe7-4850-9b33-a2196807fb01 /boot btrfs defaults 0 0
# UUID=4FD9-A843 /boot/efi vfat umask=0077,shortname=winnt 0 2
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /home btrfs subvol=@home,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
# UUID=11fa3de5-bfb4-4227-a5ee-d8a3d2d2304a /btrfs_pool btrfs subvolid=5,ssd,noatime,space_cache,commit=120,compress=zstd,discard=async,x-systemd.device-timeout=0 0 0
Also add an entry for the crypt-boot container to /etc/crypttab:
blkid | grep vda2
# UUID="84fbddb8-984c-4dda-b5b7-27a49864b5f8" TYPE="crypto_LUKS" PARTUUID="e4007823-54ac-4bce.929d-7616a04f59a8"
nano /etc/crypttab
# luks-6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf UUID=6e7e8f26-4f38-468e-aa2c-9ddaaad4aedf none discard
# crypt-boot UUID=84fbddb8-984c-4dda-b5b7-27a49864b5f8 none discard
Let’s remount the boot partition, restore the backup, and mount the efi partition:
mount /boot
rsync -avuP /boot.bak/ /boot/
mount /boot/efi
Enable cryptodisk in GRUB
echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
Regenerate the initram disk and Grub
dracut --force --regenerate-all --verbose
grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
Reboot and check whether this works
Grub should ask for the password for the boot partition and then for the root partition. You will then also need to reenter your password for the root partition. If the system then starts, the change is successful, but we need to simplify the passphrase situation using a keyfile for filesystem root (or clevis package and a tpm).
Get rid of additional passphrase prompts [WIP]
Not yet… Write me an email, if you know how!
FINISHED! CONGRATULATIONS AND THANKS FOR STICKING THROUGH!