tim olson

A site reliability engineer targeting all things Kubernetes. Coming from SWE I love working all parts of the stack. I specifically provide Kubernetes as a platform along with the administration, onboarding, and life-cycle of applications. Long live Linux.

St. Paul, MN

Installing Btrfs and Luks on Arch Linux

Wed, Jan 12, 2022

Estimated reading time: 17 min

linux desktop arch encryption luks btrfs server linux

I recently reimaged my homelab hypervisor. I for quite a few years have run a Debian then Ubuntu Server box with libvirt/QEMU/KVM on top. It’s time for a seachange. I’ve been on the same Arch desktop install across 2 motherboards and 6 years without a single breaking upgrade. So with me redoing my install, it’s time to do a general guide to arch for desktop or server.

This guide is going to be focused more on the desktop side of things. Perhaps someday I’ll discuss how my server is provisioned, but for a teaser: ansible to setup; to setup libvirt/QEMU/KVM, to install packages (ovmf, zfs userland), to perform upgrades, and to setup the networking. But that is for another day.

My guide through this blog post will be a QEMU/KVM and so things like disks or networking are likely very different.

And before we begin, surely the right place to go is the official install guide: https://wiki.archlinux.org/title/Installation_guide

Getting started #

I’ll assume you have flashed an Arch iso, and you are sitting with a Linux prompt not too unlike:

root@archiso ~ #

This guide is primarily focused on btrfs, luks, and my initial OS configuration, three things not exceptionally documented on the Arch wiki. If you don’t know how to flash the iso, it maybe be best to turn back and learn how dd or tools like etcher work.

SSHing into Archiso #

The Archiso is a live bootable environment that is stored on the installation media, which is most likely a flash drive, but historically we would refer to this as a LiveCD.

During the installation of the OS, we will need an internet connection. This provides a good opportunity to use SSH. If you have another computer (how else are you reading this), being able to use a terminal over SSH is likely more efficient than the default tty.

With our prompt let’s setup a new password, make it anything, this isn’t what your installing user will use.

root@archiso ~ # passwd
New password:
Retype new password:
passwd: password updated successfully

Check your IP:

root@archiso ~ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:d0:69:2d brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.130/24 metric 100 brd 192.168.122.255 scope global dynamic enp1s0
       valid_lft 2853sec preferred_lft 2853sec
    inet6 fe80::5054:ff:fed0:692d/64 scope link
       valid_lft forever preferred_lft forever

If you don’t see a network interface like enp1s0 you will need to troubleshoot networking, use a hardline if you can, most wifi drivers need to be installed later.

Then start ssh:

root@archiso ~ # systemctl start sshd

And login from a remote system:

[tolson@magni ~]$ ssh root@192.168.122.130

Note your IP is different.

Let’s get into it.

0. Setup and considerations #

It’s very important to highlight here that the changes we are making alter the disk and are not reversible. Take care in these steps. I recommend not following this guide on a dual boot system if this is your first try. Or at least, ensure you know which disk is being used where, or disconnect it during this process.

Analyze disks #

Let’s look at our disks by running two commands:

  • lsblk
root@archiso ~ # lsblk
NAME  MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0   7:0    0 657.4M  1 loop /run/archiso/airootfs
sr0    11:0    1 812.3M  0 rom  /run/archiso/bootmnt
vda   254:0    0    20G  0 disk
  • fdisk -l
root@archiso ~ # fdisk -l
Disk /dev/vda: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop0: 657.39 MiB, 689319936 bytes, 1346328 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Here we can see a list of block devices and a list of current partitions. In the case of my machine. /dev/loop0 is the Archiso, and /dev/vda is our virtual hard disk, that we will be installing to.

When following this tutorial on QEMU/KVM, if you see vda disks, you probably are not using UEFI, which is required by this tutorial (in that case install OVMF).

But what about physical systems and various disks?

If you’re installing onto an NVME or SSD you might see:

# NVME
NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1     259:0    0 931.5G  0 disk
NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
sda         8:16     1 465.8G  0 disk

What if you have other drives and they are formatted?

NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
sda           8:0    1   915G  0 disk
├─sda1        8:1    1   450G  0 part
└─sda2        8:18   1 465.7G  0 part

Output like this would suggest your drives are already provisioned. Cross reference with fdisk output to see if you know the filesystem type:

Disk /dev/sdb: 465.76 GiB, 500107862016 bytes, 976773168 sectors
Disk model: Samsung SSD 840
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: E4CC0E6C-2095-46C7-9B72-DB96F5BC1EC6

Device     Start       End   Sectors   Size Type
/dev/sdb1   2048     34815     32768    16M Microsoft reserved
/dev/sdb2  34816 976771071 976736256 465.7G Microsoft basic data

1. Partition #

Again, what we do beyond here is potentially dangerous.

EFI partition #

Remember /dev/vda is my install disk target, yours may be different.

You will be doing the instructions after each Command (m for help): or prompt stanza.

t@archiso ~ # fdisk /dev/vda

Welcome to fdisk (util-linux 2.37.3).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x06c0fd55.

Command (m for help): g
Created a new GPT disklabel (GUID: CC8998D4-5773-934D-B07B-CB941BE9F5D0).

Command (m for help): n
Partition number (1-128, default 1): 1
First sector (2048-41943006, default 2048): <Press Your Enter Key>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-41943006, default 41943006): +100M

Created a new partition 1 of type 'Linux filesystem' and of size 100 MiB.

Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.

You should after entering all of this still have a Command(m for help): prompt in front of you, but let’s think about where we are. We just created an EFI partition which is a small partition where our bootloader and other startup applications and data are stored. Learn more about this model here.

We pressed Enter for the first sector because we wanted to start at the nearest available space.

Boot partition #

Command (m for help): n
Partition number (2-128, default 2): 2
First sector (206848-41943006, default 206848): <Press Your Enter Key>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (206848-41943006, default 41943006): +300M

Created a new partition 2 of type 'Linux filesystem' and of size 300 MiB.

We’ve now just created a boot partition that will be used for our GRUB install.

This includes:

  • Our GRUB config
  • vmzlinux - our statically linked linux kernel
  • initramfs-linux.img - our initial RAM file system which will hand off its temporary root filesystem to our filesystem

If these parts don’t make sense follow the links, but for now don’t worry about it, the later steps take care of these pieces.

BTRFS partition #

Command (m for help): n
Partition number (3-128, default 3): 3
First sector (821248-41943006, default 821248): <Press Your Enter Key>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (821248-41943006, default 41943006): <Press Your Enter Key>

Created a new partition 3 of type 'Linux filesystem' and of size 19.6 GiB.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Just like prior steps but we hit enter twice to take up the nearest sector and all remaining space. Then we write to disk.

Command recap #

This will not be done for later steps but because we had so much terminal output and explanation, here’s every keystroke you should have applied.

g
n
1
<Enter Key>
+100M
t
1
n
2
<Enter Key>
+300M
n
3
<Enter Key>
<Enter Key>
w

To swap or not #

TLDR: This guide will not provide this instruction, but you are free to explore it. Be warmed.

Some guides will recommend swap space. I chose not to because rarely do I need it, and if you do there is no discernible difference between a swap partition or a swap file*. If you want a swap partition in between Boot Partition and BTRFS Partition add a SWAP partition equal to two times your ram (a general rule of thumb).

However later steps will not refer to this partition method, refer to the linked documentation and pay close attention to if the swap exists in your fstab later, and that you’ve run mkswap /dev/<your swap partition

* How I do swap:

  • Create a swapfile: touch /swapfile
  • Disable BTRFS Copy-On-Write: chattr +C /swapfile
  • Permissions: chmod 0600 /swapfile
  • Size swap appropriately: fallocate -l 32G /swapfile
  • Make swap: mkswap /swapfile
  • Turn on: swapon /swapfile

This can get more interesting with subvolume swap partitions but this works just fine.

2. Boot Filesystems #

Remember your partitions are numbered the same but partition 1 for me appears as: /dev/vda1

Format EFI partition #

root@archiso ~ # mkfs.vfat -F32 /dev/vda1
mkfs.fat 4.2 (2021-01-31)

Format Boot partition #

root@archiso ~ # mkfs.ext2 /dev/vda2
mke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done
Creating filesystem with 307200 1k blocks and 76912 inodes
Filesystem UUID: b42448a0-1df3-4865-89af-00eb3c8e786c
Superblock backups stored on blocks:
        8193, 24577, 40961, 57345, 73729, 204801, 221185

Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

3. Format BTRFS partition #

Setup LUKS #

root@archiso ~ # cryptsetup -y -v --cipher=aes-xts-plain64 --key-size=512 --hash=sha512 luksFormat /dev/vda3

WARNING!
========
This will overwrite data on /dev/vda3 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/vda3:
Verify passphrase:
Key slot 0 created.
Command successful.
cryptsetup -y -v --cipher=aes-xts-plain64 --key-size 512 --hash=sha512    7.48s user 0.93s system 45% cpu 18.399 total

This setups our LUKS encrypted volume. The manual page man cryptsetup can be useful, so can cryptsetup benchmark for choosing an algorithm.

Mount LUKS #

root@archiso ~ # cryptsetup luksOpen /dev/vda3 luks

It can be a fun checkpoint to look at lsblk and fdisk -l for LVM and partitions, recall what it looked like at the beginning.

Format BTRFS #

root@archiso ~ # mkfs.btrfs -L archlinux /dev/mapper/luks
btrfs-progs v5.16
See http://btrfs.wiki.kernel.org for more information.

NOTE: several default settings have changed in version 5.15, please make sure
      this does not affect your deployments:
      - DUP for metadata (-m dup)
      - enabled no-holes (-O no-holes)
      - enabled free-space-tree (-R free-space-tree)

Label:              archlinux
UUID:
Node size:          16384
Sector size:        4096
Filesystem size:    19.59GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP             256.00MiB
  System:           DUP               8.00MiB
SSD detected:       no
Zoned device:       no
Incompat features:  extref, skinny-metadata, no-holes
Runtime features:   free-space-tree
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1    19.59GiB  /dev/mapper/luks

4. Create BTRFS subvolumes #

Mount BTRFS #

root@archiso ~ # mount -o compress=zstd /dev/mapper/luks /mnt

You can see the BTRFS formatted LUKS LVM device (word salad) is using Zstandard compression. Whenever we mount our root filesystem we will need to remember this, this is more complicated than mount /dev/vda /mnt.

Create subvolumes #

root@archiso ~ # cd /mnt
root@archiso /mnt # btrfs subvolume create @
Create subvolume './@'
root@archiso /mnt # btrfs subvolume create @home
Create subvolume './@home'
root@archiso /mnt # btrfs subvolume create @log
Create subvolume './@log'
root@archiso /mnt # btrfs subvolume create @srv
Create subvolume './@srv'
root@archiso /mnt # btrfs subvolume create @pkg
Create subvolume './@pkg'
root@archiso /mnt # btrfs subvolume create @tmp
Create subvolume './@tmp'
root@archiso /mnt # cd /
root@archiso / # umount /mnt
root@archiso / # mount -o compress=zstd,subvol=@ /dev/mapper/luks /mnt

Let’s take a minute to appreciate how awesome this is. For example, in the case of a laptop with limited space, having access to POSIX file namespaces instead of individual partitions can save us pain. Traditionally we might make a root partition and a home partition. Resizing these with limited space can be a problem. Other benefits with subvolumes might be things like subvolume snapshotting… Many good things with BTRFS on a workstation.

Create filesystem directories #

root@archiso / # cd /mnt
root@archiso /mnt # mkdir -p {home,srv,var/{log,cache/pacman/pkg},tmp}
root@archiso /mnt # ls
home  srv  tmp  var

Associate filesystem directories to subvolumes #

root@archiso /mnt # mount -o compress=zstd,subvol=@home /dev/mapper/luks home
root@archiso /mnt # mount -o compress=zstd,subvol=@log /dev/mapper/luks var/log
root@archiso /mnt # mount -o compress=zstd,subvol=@pkg /dev/mapper/luks var/cache/pacman/pkg
root@archiso /mnt # mount -o compress=zstd,subvol=@srv /dev/mapper/luks srv
root@archiso /mnt # mount -o compress=zstd,subvol=@tmp /dev/mapper/luks tmp
root@archiso /mnt # mkdir /mnt/boot
root@archiso /mnt # mount /dev/vda2 /mnt/boot
root@archiso /mnt # mkdir /mnt/boot/EFI
root@archiso /mnt # mount /dev/vda1 /mnt/boot/EFI

Again I’d like to recall lsblk, but this time highlight it’s interesting output for demonstration purposes:

root@archiso /mnt # lsblk
NAME     MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0      7:0    0 657.4M  1 loop  /run/archiso/airootfs
sr0       11:0    1 812.3M  0 rom   /run/archiso/bootmnt
vda      254:0    0    20G  0 disk
├─vda1   254:1    0   100M  0 part  /mnt/boot/EFI
├─vda2   254:2    0   300M  0 part  /mnt/boot
└─vda3   254:3    0  19.6G  0 part
  └─luks 252:0    0  19.6G  0 crypt /mnt/tmp
                                    /mnt/srv
                                    /mnt/var/cache/pacman/pkg
                                    /mnt/var/log
                                    /mnt/home
                                    /mnt

Cool now we can install Arch on top of our LUKS BTRFS filesystem!

5. Arch install #

Pacstrap the package bootstrapper #

We require these packages:

  • base base-devel linux-firmware grub efibootmgr dosfstools openssh os-prober mtools linux linux-headers mkinitcpio netctl dhcpcd vim zsh dialog network-manager-applet networkmanager

But if you want wifi consider:

  • wireless_tools wpa_supplicant

Supply all those packages to: pacstrap -i /mnt <the packages>

root@archiso /mnt # pacstrap -i /mnt base base-devel grub efibootmgr dosfstools openssh os-prober mtools linux linux-headers mkinitcpio netctl dhcpcd vim

==> Creating install root at /mnt
==> Installing packages to /mnt

Enter a selection (default=all):
resolving dependencies...
:: There are 2 providers available for resolvconf:
:: Repository core
   1) openresolv  2) systemd-resolvconf

Enter a number (default=1): 2
looking for conflicting packages...

Packages (157) acl-2.3.1-2  archlinux-keyring [ ... Output removed for brevity ... ]

Total Download Size:     86.35 MiB
Total Installed Size:  1240.61 MiB

:: Proceed with installation? [Y/n] y

[ ... Output removed for brevity ... ]

I like systemd-resolvconf as my DNS plugin, but maybe some healthy systemd skepticism is worth pointing out.

Generate filesystem table #

root@archiso /mnt # genfstab -U /mnt >> /mnt/etc/fstab

This fstab defines how block devices should be mounted on system startup.

cat /mnt/etc/fstab and take a peek at our previously defined LUKS LVM devices and BTRFS formatting.

Change your “root” into the bootstrap system #

root@archiso /mnt # arch-chroot /mnt
[root@archiso /]#

You can see our prompt has changed a bit. It’s now as if we were on our installed system, learn more about chroot.

Setup encryption in the initial RAM filesystem #

mkinitcpio is the tool to setup and configure the initramfs. Normally during a kernel upgrade this is managed by the system. But on our first setup, we must do the needful.

[root@archiso /]# vim /etc/mkinitcpio.conf

You will see a line like:

HOOKS=(base udev autodetect modconf block filesystems keyboard fsck)

We want it to look like:

HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)

Then generate our new initramfs.

[root@archiso /]# mkinitcpio -p linux
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
  -> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img
[ ... Output removed for brevity ... ]
==> Image generation successful

Setup locale #

We have a few steps to do. A locale needs to be set to ensure proper text rendering.

[root@archiso /]# vim /etc/locale.gen

For me, a user in the US it should look like

[ ... Output removed for brevity ... ]
#en_SG ISO-8859-1
en_US.UTF-8 UTF-8
#en_US ISO-8859-1
[ ... Output removed for brevity ... ]

Generate:

[root@archiso /]# locale-gen
Generating locales...
  en_US.UTF-8... done
Generation complete.

Setup system locale:

[root@archiso /]# localectl set-locale LANG=en_US.UTF-8
cat > /etc/locale.conf << EOF
LANG=en_US.UTF-8
EOF

Set root password #

[root@archiso /]# passwd
New password:
Retype new password:
passwd: password updated successfully

This is your root password, might not be the best security practice but I just set this to my user’s password. 💀

6. Configure boot loader #

Install #

[root@archiso /]# grub-install --target=x86_64-efi --bootloader-id=grub_uefi --recheck
Installing for x86_64-efi platform.

Let’s not forget to configure the locale:

[root@archiso /]# mkdir -p /boot/grub/locale
[root@archiso /]# cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale/en.mo

Configure LUKS and grub #

We need to grab our partition UUID.

[root@archiso /]# blkid /dev/vda3
/dev/vda3: UUID="ea3c5e4b-d8c7-4ee3-a1af-733cf80f8d44" TYPE="crypto_LUKS" PARTUUID="40d13c59-5146-0544-b694-38037ef9def0"

Edit our grub config file:

[root@archiso /]# vim /etc/default/grub

You will see a line like:

GRUB_CMDLINE_LINUX=""

We want it to look like:

GRUB_CMDLINE_LINUX="cryptdevice=UUID=ea3c5e4b-d8c7-4ee3-a1af-733cf80f8d44:root"

Of course just like with /dev/vda your UUID is going to be very different.

We also need to enable CRYPTODISK:

You will see a line like:

#GRUB_ENABLE_CRYPTODISK=y

We want it to look like:

GRUB_ENABLE_CRYPTODISK=y

Quite simple, just an uncomment.

Generate the finalized config:

[root@archiso /]# grub-mkconfig -o /boot/grub/grub.cfg

7. Test setup #

[root@archiso /]# exit
root@archiso /mnt # reboot

Disconnect your boot media.

Hold onto your butts.

This process should go smooth but look at I broke my install, if it doesn’t.

8. Finish install - setup user #

On boot we probably wont have networking going:

[root@archiso /]# systemctl start NetworkManager
[root@archiso /]# systemctl enable NetworkManager

If you’re on wifi this could introduce additional challenges but mainly we want to enable NetworkManager on boot. SSHD we probably don’t always want on. nmcli and nmtui can be useful for configuring wireless networking.

Let’s setup a user account and login. Enter back into the Arch linux shell with:

[root@archiso /]# groupadd -r autologin
[root@archiso /]# useradd -m -g users -G wheel,autologin -s /bin/zsh tolson
[root@archiso /]# passwd tolson

If you want to continue the install from another computer, let’s start SSH again:

[root@archiso /]# systemctl start sshd

And on remote machine: ssh tolson as normal.

Now we need to setup the wheel, or sudoers permissions. Let’s uncomment the wheel permission group.

[root@archiso /]# vim /etc/sudoers

You will see a line like:

# %wheel ALL=(ALL:ALL) ALL

We want it to look like:

%wheel ALL=(ALL) ALL

9. Setup a login manager #

This guide is for base system setup but we will discuss briefly system setup.

[tolson@magni ~]$ sudo pacman -S lightdm lightdm-gtk-greeter accountsservice xorg-server
[tolson@magni ~]$ sudo systemctl enable lightdm

Then add to the greeter config:

[tolson@magni ~]$ sudo vim /etc/lightdm/lightdm.conf
[Seat:*]
autologin-user=tolson

10. Install a Desktop Environment #

This guide is not specifically focused on this part. Check out these pages:

I personally use i3, and sway (mostly i3 for now). To see my specific install go look at my dotfiles: https://github.com/tolson-vkn/dotfiles/tree/master/i3

Install the base DE from the command line and then restart. Should be good from here!

Tips #

Here are some handy things along the way.

You should use timeshift #

Didn’t have a great spot for this but timeshift is fantastic. I like to configure with daily snapshots.

This setup makes arch, rolling distributions, or really any Linux distro painless for upgrades. Using tech like grub-btrfs you can make your snapshots appear in your grub menu. So your workflow for full system (specifically kernel) upgrades is to create snapshot, and then upgrade. If anything breaks just go back.

I broke my install #

Run this to mount your system and get back in action:

root@archiso /mnt # cryptsetup luksOpen /dev/vda3 luks
root@archiso /mnt # mount -o compress=zstd,subvol=@ /dev/mapper/luks /mnt
root@archiso /mnt # mount -o compress=zstd,subvol=@home /dev/mapper/luks /mnt/home
root@archiso /mnt # mount -o compress=zstd,subvol=@log /dev/mapper/luks /mnt/var/log
root@archiso /mnt # mount -o compress=zstd,subvol=@pkg /dev/mapper/luks /mnt/var/cache/pacman/pkg
root@archiso /mnt # mount -o compress=zstd,subvol=@srv /dev/mapper/luks /mnt/srv
root@archiso /mnt # mount -o compress=zstd,subvol=@tmp /dev/mapper/luks /mnt/tmp
root@archiso /mnt # mount /dev/vda2 /mnt/boot
root@archiso /mnt # mount /dev/vda1 /mnt/boot/EFI
root@archiso /mnt # arch-chroot /mnt

Missing foreign languages #

Using the yay AUR package manager, install:

yay -S ttf-ancient-fonts ttf-ubraille adobe-source-han-sans-otc-fonts  adobe-source-han-serif-otc-fonts  ttf-hannom  ttf-paratype otf-gfs opensiddur-hebrew-fonts persian-fonts fonts-tlwg ttf-tibetan-machine

Alacritty not showing emoji #

I use Alacritty as my terminal. I noticed problems where emoji would show in the default font not my specifically installed noto-fonts-emoji package.

The solution was to add this to the file: /etc/fonts/fonts.conf, within <fontconfig></fontconfig>:

<!-- Fallback fonts preference order -->
        <alias>
                <family>sans-serif</family>
                <prefer>
                        <family>Noto Color Emoji</family>
                        <family>Noto Sans</family>
                        <family>Open Sans</family>
                        <family>Droid Sans</family>
                        <family>Roboto</family>
                        <family>Tholoth</family>
                        <family>Noto Sans Arabic</family>
                </prefer>
        </alias>
        <alias>
                <family>serif</family>
                <prefer>
                        <family>Noto Color Emoji</family>
                        <family>Noto Sans</family>
                        <family>Noto Serif</family>
                        <family>Droid Serif</family>
                        <family>Roboto Slab</family>
                        <family>Tholoth</family>
                        <family>Noto Sans Arabic</family>
                </prefer>
        </alias>
        <alias>
                <family>monospace</family>
                <prefer>
                        <family>Noto Color Emoji</family>
                        <family>Noto Sans</family>
                        <family>Noto Sans Mono</family>
                        <family>Inconsolata</family>
                        <family>Droid Sans Mono</family>
                        <family>Roboto Mono</family>
                </prefer>
        </alias>