How to install Gentoo with UEFI LUKS Btrfs and systemd
Introduction
In this post I’ll describe how to install Gentoo with systemd stage3 tarball on UEFI LUKS partition and Btrfs filesystem, using the standard de facto @ subvolume as root file system.
I’ve also written two different guides to install Gentoo on LUKS, but using LVM Volume group, and ext4 filesystem, if you’re interested in those you can find here a guide to install on BIOS partition, and here a guide to install on UEFI partition.
UPDATE 23/08/2023: Correct typo on GRUB_PLATFORMS
Disk partitions
I’m using to create gpt partitions, with a small BIOS boot partition (2 MiB) to be used by grub for second stages of itself.
Partition scheme
This is the quite simple partition scheme used in this guide. In this guide I’m using an NVME disk /dev/nvme0n1, but if you have a scsi disk like /dev/sda, its simple as run sed ‘s/nvme0n1/sda/g’
Partition | Filesystem | Size | Description |
/dev/nvme0n1p1 | fat | 2MiB | bios boot |
/dev/nvme0n1p2 | fat32 | 800MiB | Boot partition |
/dev/nvme0n1p3 | LUKS | rest of the disk | LUKS partition |
Creating the partitions
Using parted utility now we can create all required partitions
livecd ~# parted /dev/nvme0n1
GNU Parted 3.3
Using /dev/nvme0n1
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Error: /dev/nvme0n1: unrecognised disk label
Model: WDC PC SN730 SDBQNTY-256G-1001 (nvme)
Disk /dev/nvme0n1: 256GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:
(parted) mklabel gpt
(parted) mkpart primary fat32 1MiB 3MiB
(parted) mkpart primary fat32 3MiB 803MiB
(parted) mkpart primary 803MiB -1
(parted) name 1 grub
(parted) name 2 boot
(parted) name 3 luks
(parted) set 1 bios_grub on
(parted) set 2 boot on
(parted) print
Model: WDC PC SN730 SDBQNTY-256G-1001 (nvme)
Disk /dev/nvme0n1: 256GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 3146kB 2097kB fat32 grub bios_grub
2 3146kB 842MB 839MB fat32 boot boot, esp
3 842MB 256GB 255GB luks
(parted) quit
Create fat filesystems
For both bios boot we’ll create a fat filesystem, instead, for boot partition we’ll create a fat32 filesystem:
livecd ~# mkfs.vfat /dev/nvme0n1p1
mkfs.fat 4.1 (2017-01-24)
livecd ~# mkfs.vfat -F32 /dev/nvme0n1p2
mkfs.fat 4.1 (2017-01-24)
Encrypt partition with LUKS
Now we can crypt the third partition /dev/nvme0n1p3 with LUKS
livecd ~# cryptsetup luksFormat -c aes-xts-plain64 -s 512 /dev/nvme0n1p3
WARNING!
========
This will overwrite data on /dev/nvme0n1p3 irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/nvme0n1p3:
Verify passphrase:
livecd ~#
Open the LUKS device
livecd ~# cryptsetup luksOpen /dev/nvme0n1p3 luksdev
Enter passphrase for /dev/nvme0n1p3:
livecd ~#
Create Btrfs filesystem and subvolume
I’ll create this Btrfs volume/filesytem and subvolumes
Volume | Subvolume name | Parent Volume | Mount point | Description |
/dev/nvme0n1p3 | - | - | - | Btrfs primary volume / device |
- | @ | /dev/nvme0n1p3 | / | Root filesystem |
- | @home | /dev/nvme0n1p3 | /home | home filesystem |
- | @snapshots | /dev/nvme0n1p3 | /.snapshots | snapshots filesystem |
Create the Btrfs filesystem with Gentoo label and mount under /mnt/gentoo
livecd ~# mkfs.btrfs -L Gentoo /dev/mapper/luksdev
btrfs-progs v5.4.1
See http://btrfs.wiki.kernel.org for more information.
Detected a SSD, turning off metadata duplication. Mkfs with -m dup if you want to force metadata duplication.
Label: Gentoo
UUID: a000eea9-d97c-4107-ae39-602049a6acaa
Node size: 16384
Sector size: 4096
Filesystem size: 237.67GiB
Block group profiles:
Data: single 8.00MiB
Metadata: single 8.00MiB
System: single 4.00MiB
SSD detected: yes
Incompat features: extref, skinny-metadata
Checksum: crc32c
Number of devices: 1
Devices:
ID SIZE PATH
1 237.67GiB /dev/mapper/luksdev
livecd ~# mount /dev/mapper/luksdev /mnt/gentoo
Create all subvolumes:
livecd ~# btrfs subvolume create /mnt/gentoo/@
Create subvolume '/mnt/gentoo/@'
livecd ~# btrfs subvolume create /mnt/gentoo/@home
Create subvolume '/mnt/gentoo/@home'
livecd ~# btrfs subvolume create /mnt/gentoo/@snapshots
Create subvolume '/mnt/gentoo/@snapshots'
livecd ~# btrfs subvolume list /mnt/gentoo
ID 256 gen 7 top level 5 path @
ID 257 gen 8 top level 5 path @home
ID 258 gen 9 top level 5 path @snapshots
livecd ~#
umount /mnt/gentoo, because for root filesystem we’ll use the @ subvolume:
livecd ~# umount /mnt/gentoo
and finally we can mount our root filesystem based on @ subvolume:
livecd ~# mount -t btrfs -o noatime,relatime,compress=lzo,ssd,space_cache,subvol=@ /dev/mapper/luksdev /mnt/gentoo
WARNING: if you have a recent kernel, for example 5.19 space_cache option must be space_cache=v2:
livecd ~# mount -t btrfs -o noatime,relatime,compress=lzo,ssd,space_cache=v2,subvol=@ /dev/mapper/luksdev /mnt/gentoo
Gentoo installation
Now it’s time to get your hands dirty.
Install systemd stage3
Download the systemd stage3 tarball from Gentoo
livecd ~# cd /mnt/gentoo/
livecd /mnt/gentoo # wget https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20201222T005811Z/stage3-amd64-systemd-20201222T005811Z.tar.xz
Unarchive the tarball
livecd /mnt/gentoo # tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner
Configuring compile options
Open /mnt/gentoo/etc/portage/make.conf file and configure the system with your preferred optimization variables. Have a look at Gentoo Handbook
livecd /mnt/gentoo # vi /mnt/gentoo/etc/portage/make.conf
For example, below you can find my make.conf optimization variables
# These settings were set by the catalyst build script that automatically
# built this stage.
# Please consult /usr/share/portage/config/make.conf.example for a more
# detailed example.
COMMON_FLAGS="-march=skylake -O2 -pipe"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
CPU_FLAGS_X86="aes avx avx2 f16c fma3 mmx mmxext pclmul popcnt sse sse2 sse3 sse4_1 sse4_2 ssse3"
MAKEOPTS="-j8"
GRUB_PLATFORMS="efi-64"
L10N="en it"
USE="-gtk -gnome systemd networkmanager pulseaudio spice usbredir udisks offensive cryptsetup ocr bluetooth bash-completion opengl opencl vulkan v4l x265 theora policykit vaapi vdpau lto cec cameras_ptp2 wayland"
POLICY_TYPES="targeted"
INPUT_DEVICES="libinput"
ACCEPT_KEYWORDS="~amd64"
ACCEPT_LICENSE="* -@EULA"
VIDEO_CARDS="intel i965 iris"
LLVM_TARGETS="BPF WebAssembly"
PYTHON_TARGETS="python2_7 python3_7 python3_8 python3_9"
# NOTE: This stage was built with the bindist Use flag enabled
PORTDIR="/var/db/repos/gentoo"
DISTDIR="/var/cache/distfiles"
PKGDIR="/var/cache/binpkgs"
# This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
LC_MESSAGES=C
Chrooting
Copy DNS configurations:
livecd /mnt/gentoo # cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
Mount proc, dev and shm filesystems:
livecd /mnt/gentoo # mount -t proc /proc /mnt/gentoo/proc
livecd /mnt/gentoo # mount --rbind /sys /mnt/gentoo/sys
livecd /mnt/gentoo # mount --make-rslave /mnt/gentoo/sys
livecd /mnt/gentoo # mount --rbind /dev /mnt/gentoo/dev
livecd /mnt/gentoo # mount --make-rslave /mnt/gentoo/dev
livecd /mnt/gentoo # test -L /dev/shm && rm /dev/shm && mkdir /dev/shm
livecd /mnt/gentoo # mount -t tmpfs -o nosuid,nodev,noexec shm /dev/shm
livecd /mnt/gentoo # chmod 1777 /dev/shm
chroot to /mnt/gentoo:
livecd /mnt/gentoo # chroot /mnt/gentoo /bin/bash
livecd / # source /etc/profile
livecd / # export PS1="(chroot) $PS1"
(chroot) livecd / #
mounting the boot partition:
(chroot) livecd / # mount /dev/nvme0n1p2 /boot
and then mount /home and /.snapshots subvolumes:
(chroot) livecd / # mkdir /.snapshots
(chroot) livecd / # mount -t btrfs -o noatime,relatime,compress=lzo,ssd,subvol=@snapshots /dev/mapper/luksdev /.snapshots
(chroot) livecd / # mount -t btrfs -o noatime,relatime,compress=lzo,ssd,subvol=@home /dev/mapper/luksdev /home
Updating the Gentoo ebuild repository
Update the Gentoo ebuild repository to the latest version:
(chroot) livecd ~# emerge --sync
Update portage
If at the end of emerge sync you see a message like this:
Action: sync for repo: gentoo, returned code = 0
* An update to portage is available. It is _highly_ recommended
* that you update portage now, before any other packages are updated.
* To update portage, run 'emerge --oneshot sys-apps/portage' now.
if you are on this case emerge the portage:
(chroot) livecd ~# emerge --oneshot sys-apps/portage
Choosing the right profile (with systemd)
Choose one of the systemd profile available, for example for my system I have selected desktop/plasma/systemd
(chroot) livecd ~ # eselect profile list
...
(chroot) livecd ~ # eselect profile set 9
(chroot) livecd ~ # eselect profile list
Available profile symlink targets:
...
[9] default/linux/amd64/17.1/desktop/plasma/systemd (stable) *
...
Timezone
Update the timezone, for example Europe/Rome
(chroot) livecd ~# echo Europe/Rome > /etc/timezone
(chroot) livecd ~# emerge --config sys-libs/timezone-data
Configure locales
If you want only few locales on your system, for example C, en_us and it_IT
(chroot) livecd /etc # cat locale.gen
...
C.UTF8 UTF-8
en_US ISO-8859-1
en_US.UTF-8 UTF-8
it_IT ISO-8859-1
it_IT.UTF-8 UTF-8
(chroot) livecd /etc # locale-gen
now you can choose your preferred locale with
(chroot) livecd /etc # eselect locale list
(chroot) livecd /etc # eselect locale set 10
(chroot) livecd /etc # eselect locale list
...
[10] C.UTF8 *
reload the environment
(chroot) livecd /etc # env-update && source /etc/profile && export PS1="(chroot) $PS1"
Updating the world
If you change your profile, or if you change your USE flags run the update
(chroot) livecd ~# mkdir -p /etc/portage/package.{accept_keywords,license,mask,unmask,use}
(chroot) livecd ~# time emerge --ask --verbose --update --deep --with-bdeps=y --newuse --keep-going --backtrack=30 @world
now you can take a coffee
Optional - GCC Upgrade
If you are in amd64 testing, most probably, updating the world, you have installed a new version of GCC, so from now we can use it
(chroot) livecd ~# gcc-config --list-profiles
[1] x86_64-pc-linux-gnu-9.3.0 *
[2] x86_64-pc-linux-gnu-10.2.0
set the default profile to 2, corresponding to the above example to the GCC 10.2
(chroot) livecd ~# gcc-config 2
* Switching native-compiler to x86_64-pc-linux-gnu-10.2.0 ...
>>> Regenerating /etc/ld.so.cache... [ ok ]
* If you intend to use the gcc from the new profile in an already
* running shell, please remember to do:
* . /etc/profile
(chroot) livecd ~# source /etc/profile
livecd ~# export PS1="(chroot) $PS1"
after that re-emerge the libtool:
(chroot) livecd ~# emerge --ask --oneshot --usepkg=n sys-devel/libtool
Optional - Install vim
If you hate nano editor like me, now you can install vim:
(chroot) livecd ~# emerge --ask app-editors/vim
Configure fstab
This file contains the mount points of partitions to be mounted. Run blkid to see the UUIDs:
(chroot) livecd ~# blkid
/dev/loop0: TYPE="squashfs"
/dev/nvme0n1p1: SEC_TYPE="msdos" UUID="1C3B-C680" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="grub" PARTUUID="39bd8ab8-3708-4ecf-b4e3-f5714a6e4ea1"
/dev/nvme0n1p2: UUID="1D97-3854" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="boot" PARTUUID="b44718e2-d7fe-4eba-a6c7-1d1beee11806"
/dev/nvme0n1p3: UUID="1ce717f4-5a82-49e7-ae1c-9a92e4c20251" TYPE="crypto_LUKS" PARTLABEL="luks" PARTUUID="c271c93e-6f59-446f-9139-a0b98afab820"
/dev/sda1: BLOCK_SIZE="2048" UUID="2020-12-22-12-44-06-70" LABEL="Gentoo amd64 20201222T005811Z" TYPE="iso9660" PTUUID="7437c9e0" PTTYPE="dos" PARTUUID="7437c9e0-01"
/dev/sda2: SEC_TYPE="msdos" LABEL_FATBOOT="GENTOOLIVE" LABEL="GENTOOLIVE" UUID="A168-D76E" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="7437c9e0-02"
/dev/mapper/luksdev: LABEL="Gentoo" UUID="a000eea9-d97c-4107-ae39-602049a6acaa" UUID_SUB="d45b2afd-7250-4ba1-a896-b0e81a20fa4b" BLOCK_SIZE="4096" TYPE="btrfs"
copy the UUID for the root filesystem upon luksdev device, (in the above example is a000eea9-d97c-4107-ae39-602049a6acaa), also copy the UUID for the boot filesystem which resides on /dev/nvme0n1p2 partition (on the above example is 1D97-3854).
This is my fstab
(chroot) livecd /etc # cat fstab
# /etc/fstab: static file system information.
UUID=1D97-3854 /boot vfat noatime 0 1
UUID=a000eea9-d97c-4107-ae39-602049a6acaa / btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@ 0 0
UUID=a000eea9-d97c-4107-ae39-602049a6acaa /home btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@home 0 0
UUID=a000eea9-d97c-4107-ae39-602049a6acaa /.snapshots btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@snapshots 0 0
# tmps
tmpfs /tmp tmpfs defaults,size=4G 0 0
tmpfs /run tmpfs size=100M 0 0
# shm
shm /dev/shm tmpfs nodev,nosuid,noexec 0 0
WARNING I’ve been using the option discard=async since I’m using kernel greater then 5.6, if you’re using kernel 5.4.x (or older) don’t use the discard=async option!!!
Installing the sources
Install the kernel, genkernel and cryptsetup:
(chroot) livecd ~# emerge --ask sys-kernel/gentoo-sources
(chroot) livecd ~# emerge --ask sys-kernel/genkernel
(chroot) livecd ~# emerge --ask sys-fs/cryptsetup
Optional - Installing firmware
Some drivers require additional firmware, if you use some of those you need to install the firmware packages:
(chroot) livecd ~# emerge --ask sys-kernel/linux-firmware
Optional - Installing intel microcode
If you have an Intel cpu and you want to upgrade the microcode you could install the intel-microcode package:
(chroot) livecd ~# mkdir -p /etc/portage/package.use
(chroot) livecd ~# echo "sys-firmware/intel-microcode initramfs" > /etc/portage/package.use/intel-microcode
(chroot) livecd ~# emerge --ask sys-firmware/intel-microcode
Configure genkernel.conf
Configure genkernel for systemd, LUKS and Btrfs:
(chroot) livecd ~# cd /etc/
(chroot) livecd /etc # cp -p genkernel.conf genkernel.conf.ORIG
(chroot) livecd /etc # vim genkernel.conf
...
MAKEOPTS="$(portageq envvar MAKEOPTS)"
...
LUKS="yes"
...
BTRFS="yes"
...
Run genkernel
Configure your kernel with the preferred options, and then
(chroot) livecd ~# time genkernel all
Install and configure grub
Install grub
Configure grub to use efi-64 platform:
(chroot) livecd ~# echo 'GRUB_PLATFORMS="efi-64"' >> /etc/portage/make.conf
emerge grub:
(chroot) livecd ~# emerge --ask sys-boot/grub
Install grub
just run grub-install:
(chroot) livecd ~# grub-install --target=x86_64-efi --efi-directory=/boot
Configure grub
First find the LUKS UUID and the root filesystem UUID:
(chroot) livecd ~# blkid | egrep '(crypto_LUKS|luksdev)'
/dev/nvme0n1p3: UUID="1ce717f4-5a82-49e7-ae1c-9a92e4c20251" TYPE="crypto_LUKS" PARTLABEL="luks" PARTUUID="c271c93e-6f59-446f-9139-a0b98afab820"
/dev/mapper/luksdev: LABEL="Gentoo" UUID="a000eea9-d97c-4107-ae39-602049a6acaa" UUID_SUB="d45b2afd-7250-4ba1-a896-b0e81a20fa4b" BLOCK_SIZE="4096" TYPE="btrfs"
In my case the LUKS UUID is 1ce717f4-5a82-49e7-ae1c-9a92e4c20251 and the root UUID is a000eea9-d97c-4107-ae39-602049a6acaa
Edit /etc/default/grub
(chroot) livecd ~# cd /etc/default/
(chroot) livecd /etc/default # cp -p grub grub.ORIG
(chroot) livecd /etc/default # vim grub
and most important configure GRUB_CMDLINE_LINUX with
GRUB_CMDLINE_LINUX="init=/usr/lib/systemd/systemd crypt_root=UUID=1ce717f4-5a82-49e7-ae1c-9a92e4c20251 root=UUID=a000eea9-d97c-4107-ae39-602049a6acaa rootflags=subvol=@"
where
parameter | description |
init | set init to systemd -> /usr/lib/systemd/systemd |
crypt_root | put here the LUKS UUID (from blkid) of the third partition /dev/nvme0n1p3 |
root | put here the Btrfs UUID (kept from luksdev device on blkid) |
rootflags | tell the kernel what Btrfs’ subvolume contains root filesystem (@) |
finally we can run grub-mkconfig:
(chroot) livecd /etc/default # cd
(chroot) livecd ~# grub-mkconfig -o /boot/grub/grub.cfg
Set the root password
Remember to set the root password:
(chroot) livecd ~# passwd
Rebooting the system
Exit the chrooted environment and reboot:
(chroot) livecd ~# exit
livecd /mnt/gentoo # shutdown -r now
Optional: Add swap subvolume and swapfile
If you want to add a swapfile, inside your Btrfs filesystem, after the first reboot, check at first, the LUKS device mapper created by your initramfs, if you’re using genkernel this device mapper is called root:
~# blkid | grep Gentoo
/dev/mapper/root: LABEL="Gentoo" UUID="a000eea9-d97c-4107-ae39-602049a6acaa" UUID_SUB="d45b2afd-7250-4ba1-a896-b0e81a20fa4b" BLOCK_SIZE="4096" TYPE="btrfs"
now mount it under /mnt/gentoo directory:
~# mkdir -p /mnt/gentoo
~# mount /dev/mapper/root /mnt/gentoo
create the @swap subvolume:
~# btrfs subvolume create /mnt/gentoo/@swap
Create subvolume '/mnt/gentoo/@swap'
create the swapfile, in my case 4G of swapfile, but you can adapt it as your needs:
~# truncate -s 0 /mnt/gentoo/@swap/swapfile
disable the copy on write and Btrfs compression:
~# chattr +C /mnt/gentoo/@swap/swapfile
~# btrfs property set /mnt/gentoo/@swap/swapfile compression none
and create the 4G swapfile:
~# fallocate -l 4G /mnt/gentoo/@swap/swapfile
~# chmod 0600 /mnt/gentoo/@swap/swapfile
~# mkswap /mnt/gentoo/@swap/swapfile
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
create the mountpoint /.swap:
~# mkdir /.swap
and add two rows in the fstab, one for the @swap subvolume, and the second one for the swapfile /.swap/swapfile
~# cd /etc
/etc# cat fstab
# /etc/fstab: static file system information.
UUID=1D97-3854 /boot vfat noatime 0 1
UUID=a000eea9-d97c-4107-ae39-602049a6acaa / btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@ 0 0
UUID=a000eea9-d97c-4107-ae39-602049a6acaa /home btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@home 0 0
UUID=a000eea9-d97c-4107-ae39-602049a6acaa /.snapshots btrfs noatime,relatime,compress=lzo,ssd,discard=async,subvol=@snapshots 0 0
UUID=a000eea9-d97c-4107-ae39-602049a6acaa /.swap btrfs noatime,relatime,compress=no,ssd,discard=async,subvol=@swap 0 0
/.swap/swapfile none swap sw 0 0
# tmps
tmpfs /tmp tmpfs defaults,size=4G 0 0
tmpfs /run tmpfs size=100M 0 0
# shm
shm /dev/shm tmpfs nodev,nosuid,noexec 0 0
remember to set compress=no to speeding up the swapfile and set discard=async only if have a kernel > 5.6.
Final umount /mnt/gentoo and reboot:
~# umount /mnt/gentoo
~# reboot