Published: July 6th, 2024
Last updated: July 3rd, 2025
Setting up Gentoo on a Raspberry Pi 4B with full disk encryption took me about a week, but I’m happy with the result and learned several things along the way. I want to share my findings so others don’t have to spend as much time figuring this out.
Note that this provides a rough outline, not a step-by-step guide for installing Gentoo.
Here are the components my setup uses:
I could have used a monitor and keyboard instead of a serial connection. This would have been easier overall. Since I planned to use the Raspberry Pi headlessly, I wanted to learn how to set it up without a keyboard and monitor.
I mainly followed Gentoo’s official Raspberry Pi Install Guide and Raspberry Pi4 64 Bit Install to get an initial Gentoo installation on a microSD card. I want to discuss some difficulties I encountered during this process.
The most challenging parts of the installation were getting wireless networking and the serial console working. I’ll cover each separately.
Getting wireless networking functional required handling a couple of tasks correctly:
Download the firmware, place it in the appropriate locations, and
create symbolic links. The Gentoo wiki covers this
process.
I found that referencing the Raspberry Pi Operating System (OS)
directory listing for /usr/lib/firmware/brcm/
helped me understand the
symbolic link naming scheme.
Here’s what a working /usr/lib/firmware/brcm/
looks like for me
(though note that I haven’t tested bluetooth):
$ ls -lh /usr/lib/firmware/brcm/
total 704K
-rw-r--r-- 1 root root 63K Jun 24 12:07 BCM4345C0.hcd
lrwxrwxrwx 1 root root 13 Jun 24 12:07 BCM4345C0.raspberrypi,4-model-b.hcd -> BCM4345C0.hcd
-rw-r--r-- 1 root root 629K Jun 24 12:04 brcmfmac43455-sdio.bin
-rw-r--r-- 1 root root 2.7K Jun 24 12:05 brcmfmac43455-sdio.clm_blob
lrwxrwxrwx 1 root root 22 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.bin -> brcmfmac43455-sdio.bin
lrwxrwxrwx 1 root root 27 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.clm_blob -> brcmfmac43455-sdio.clm_blob
lrwxrwxrwx 1 root root 22 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.txt -> brcmfmac43455-sdio.txt
-rw-r--r-- 1 root root 2.1K Jun 24 12:05 brcmfmac43455-sdio.txt
Install networking software like net-misc/dhcpcd
and
net-misc/wpa_supplicant
, then configure the software as needed. The
Gentoo handbook
documents these steps.
Despite the second step appearing straightforward, I encountered an issue. How do I install networking tools when I need those same tools to configure networking?
For many people, the easiest solution would be plugging in an Ethernet
cable and installing packages that way. I also read that downloading the
distfiles and copying them over manually works, since in theory,
emerge
won’t need to download anything.
I already knew that I wouldn’t have Ethernet access, so I tried the distfiles method and received this error:
root@nasberry /etc/portage # emerge --ask net-misc/dhcpcd net-wireless/wpa_supplicant
!!! /etc/portage/make.profile is not a symlink and will probably prevent most merges.
!!! It should point into a profile within /var/db/repos/gentoo/profiles/
!!! (You can safely ignore this message when syncing. It's harmless.)
!!! Your current profile is invalid. If you have just changed your profile
!!! configuration, you should revert back to the previous configuration.
!!! Allowed actions are limited to --help, --info, --search, --sync, and
!!! --version.
After investigating further, I realized that /var/db/repos/gentoo
didn’t exist. I would need to run emerge-webrsync
to get it, which
requires networking.
What worked for me was to plug the microSD card into a USB adapter, mount the root filesystem on my laptop, create a Quick Emulator (QEMU) chroot, and then compile the software I needed inside the chroot using my laptop’s networking.
To get the serial console working, I completed these steps:
I ensured that enable_uart=1
appeared in /boot/config.txt
. I
found this in the Raspberry Pi documentation for
config.txt
.
I added console=tty console=serial0,115200
to /boot/cmdline.txt
.
cmdline.txt
documentation is available
here.
Note that the order matters because whichever console=
entry
appears last becomes the destination for /dev/console
. I discovered
this because mine were in the wrong order and the Linux Unified Key
Setup (LUKS) decryption prompt, as well as OpenRC output, never
appeared on my serial console.
More information is available at the kernel.org documentation for the serial console.
For early console
support,
I added earlycon=uart8250,mmio32,0xfe215040
to /boot/cmdline.txt
.
To get a tty, I uncommented the line in /etc/inittab
that points to
the /dev/ttyS0
device.
# SERIAL CONSOLES
s0:12345:respawn:/sbin/agetty -L 115200 ttyS0 vt100
#s1:12345:respawn:/sbin/agetty -L 115200 ttyS1 vt100
At this point, I had a fully functioning Gentoo installation with wireless networking and serial console access on the microSD card. The next challenge was transferring the root filesystem to an encrypted SSD and getting it working.
The approach I used involves having the Raspberry Pi 4B read firmware
files and everything else needed to boot from the unencrypted Extensible
Firmware Interface (EFI) partition on the microSD card.
/boot/config.txt
and /boot/cmdline.txt
contain configuration entries
and parameters relevant to the boot process.
I securely wiped the disk by overwriting the SSD with random data to hinder cryptanalysis and performed a memory cell clearing (also known as “secure erase”).
After that, I created a LUKS volume with Logical Volume Manager (LVM) inside it on the SSD. Then I created filesystems on the logical volumes.
I ensured that the kernel included support for relevant technologies. In my case, this included LVM, dm-crypt, and X File System (XFS), as the system needs these during the boot process.
You can choose between sys-kernel/raspberrypi-sources
and
sys-kernel/gentoo-sources
. I can confirm that both work, although I
started with sys-kernel/raspberrypi-sources
first.
sys-kernel/gentoo-sources
lacks some features like device tree overlay
support, but it works for my use case.
Other kernels like the distribution kernel could work, but I haven’t tested them.
I followed instructions from the Linux kernel section of the Raspberry Pi docs, adjusting where necessary:
Create a backup of the previous kernel image if present.
# cp /boot/kernel8.img /boot/kernel8.img.bak
Change directory to kernel sources.
# cd /usr/src/linux
Build the kernel. The Raspberry Pi docs recommend setting make jobs to 1.5x the number of processors.
# make -j6 Image.gz modules dtbs
Install the kernel modules. Complete this step before generating the initramfs, since the initramfs uses the modules created from this step.
# make -j6 modules_install
Run make install
to generate the initramfs since you’ll need it to
decrypt and mount the root filesystem. -j6
doesn’t seem to provide any
benefit here.
# make install
Copy necessary files to the correct locations.
# cp arch/arm64/boot/Image.gz /boot/kernel8.img
# cp arch/arm64/boot/dts/broadcom/*.dtb /boot/
# cp arch/arm64/boot/dts/overlays/*.dtb* /boot/overlays/
# cp arch/arm64/boot/dts/overlays/README /boot/overlays/
Update /etc/fstab
to include the NVMe disk instead of the SD card.
#/dev/mmcblk0p3 / ext4 defaults 0 0
/dev/gentoolvm/root / xfs defaults 0 1
Update /boot/config.txt
. The system will automatically select
kernel8.img
as the default kernel, but you need to add the initramfs.
initramfs initramfs-6.6.31_p20240529-raspberrypi-v8.img followkernel
Update /boot/cmdline.txt
so it provides information about the root
filesystem location (root=UUID=...
), which LUKS volume to decrypt
(rd.luks.uuid=...
), and which LVM volume group to activate
(rd.lvm.vg=gentoolvm
). You can find UUIDs with blkid
.
root=UUID=... rd.luks.uuid=... rd.lvm.vg=gentoolvm
I created a full system backup of the Raspberry Pi’s microSD card with bsdtar.
# bsdtar \
--acls \
--xattrs \
--zstd \
--exclude='/dev/*' \
--exclude='/proc/*' \
--exclude='/sys/*' \
--exclude='/tmp/*' \
--exclude='/run/*' \
--exclude='/mnt/*' \
--exclude='/media/*' \
--exclude='/lost+found/' \
-cvaf /home/user/backup.tar.zst \
/
Then I unpacked it onto the SSD. I had decrypted the LUKS partition and
mounted the filesystems at /mnt
.
# cd /mnt
# bsdtar --acls --xattrs -xvpf /home/user/backup.tar.zst
After rebooting, the system presented the decryption prompt and I could boot successfully.
The Raspberry Pi foundation kernel lacks security support from Gentoo,
so I wanted to switch to sys-kernel/gentoo-sources
. This process is
straightforward.
Change directory to the new kernel sources.
$ cd /usr/src/linux-6.6.30-gentoo
Copy the working config over.
# cp /usr/src/linux-6.6.31_p20240529-raspberrypi/.config .
Update the current kernel configuration so it works with this kernel.
# make oldconfig
Follow the previous process to build the kernel, except skip copying the
overlay files since sys-kernel/gentoo-sources
doesn’t create them.
Once complete, update /boot/config.txt
so it points to the correct
initramfs file.
Another step I had to complete was updating /boot/cmdline.txt
to
include 8250.nr_uarts=1
, otherwise the serial console fails to
initialize.
This setup won’t prevent a determined adversary from removing the microSD card and tampering with anything on the boot partition (the kernel, the initramfs, or the firmware). It also doesn’t prevent hardware-level attacks. If an attacker leverages these, they can obtain the encryption password entered on boot.
Still, the encryption protects the data when the Raspberry Pi is powered off. This setup raises the difficulty level for data recovery, but it doesn’t account for sophisticated attacks like this.
If those types of attacks ever became part of my threat model, I would look into Secure Boot on the Raspberry Pi.