Just Enough Gentoo
Do you like Lego? Do you like Linux? I think I've found the most Lego-ish Linux of the bunch: Gentoo Linux!
Using the Gentoo minimal install image and Reading The Fine Manual, I show the choices I make to create an encrypted Root-On-ZFS minimal Linux system with "just enough" to provide a solid foundation to build upon further: whether that be setting up a desktop, laptop, or server.
- 01. Start Here
- 02. Prepare the DISK
- 03. Create the ZFS Pool
- 04. Install the Gentoo Base System
- 05. Configure the Linux Kernel
- 06. Configure the System
- 07. System Tools
- 08. ZFSBootMenu
- 09. Finish Up
- 10. Resources
01. Start Here
Throughout this HOWTO, if you see square brackets [] in code blocks, that means the word of code (square brackets included) should be replaced with something else. This is detailed in the instructions before or after the code block.
Gentoo Linux is installed as the sole operating system on a single disk using a three-partition layout:
- Partition
espserves as the EFI system partition and is formatted with thefat32file system. - Partition
swapprovidesswapspace with encryption courtesy ofdm-crypt. - Partition
poolcontainsrootand is formatted with thezfsfile system using native encryption.
A few assumptions:
- Target device is
amd64architecture using UEFI to boot. - Use
glibcas system libc. - Secure boot is disabled on target device.
- Installation image is prepared on a Linux/BSD system.
- Network access during install uses a wired interface.
- System does not require hibernation support.
OpenRCwill be used as system init andZFSBootMenuas bootloader.
Acquire an installation image
The current ISO install images are available for download from a selection of Gentoo mirrors. A local (Canadian) mirror that has always proven fast and reliable for me for Linux ISO and package downloads is maintained by the University of Waterloo.
From the releases/amd64/autobuilds/current-install-amd64-minimal/ directory of the mirror of choice, download:
install-amd64-minimal-[RELEASE].isoinstall-amd64-minimal-[RELEASE].iso.sha256
As of June 16 2026, [RELEASE] is 20260531T160106Z.
Verify the image integrity:
sha256sum -c --ignore-missing install-amd64-minimal-20260531T160106Z.iso.sha256Prepare the USB installation media
Write the installer to an unmounted USB storage device running the dd command as root.
ALERT
Be very careful to note the proper device (which can be identified with lsblk command). All contents on the device will be lost!
Example: On a Linux system, if a USB stick appears as sdx1, then write the installer to sdx (omit partition number):
dd bs=4M conv=fsync oflag=direct status=progress \
if=install-amd64-minimal-20260531T160106Z.iso of=/dev/sdxBoot the installation media
Boot the target device from the Gentoo installation media. Select a keyboard (example: 9 for colemak) or accept the default us.
User is automatically logged in as user root to the first virtual console.
Set larger console font
If the existing font size appears too small, running:
setfont -d
...will double the size.
Verify network connectivity
Wired network interfaces should be auto-enabled and connected at boot.
Verify the network interface is active, has been assigned an address, and the internet is reachable:
ip addr
ping -c 3 8.8.8.8
ping -c 3 gentoo.orgRemote login to installer
Make this manual installation process easier (i.e. cut-n-paste commands) by remotely logging into the installer via ssh from another computer.
Start the sshd daemon:
rc-service sshd start
Set a password for root:
passwd
Switch to the other computer and ssh into the target device:
ssh root@[ip_address]
...where [ip_address] is the target device’s address obtained with the ip addr command above.
Verify boot mode
Confirm target device is using UEFI boot mode:
cat /sys/firmware/efi/fw_platform_size
If the command returns 64, then system is booted in UEFI with 64-bit x64 UEFI and we are good to go.
ALERT
If the file does not exist, the device is not using UEFI and these instructions as outlined will not produce a working system.
Update the system clock
chronyd -q && date02. Prepare the DISK
Set up a custom partition layout on a single disk before implementing the Gentoo base installation.
Define DISK variables
Identify the disk where Gentoo will be installed by listing block devices:
lsblk -f
Set DISK variables for either a SATA or NVMe disk:
SATA
Example disk: sda
export DISK="/dev/sda"
export ESP_PART="1"
export SWAP_PART="2"
export POOL_PART="3"
export ESP_DEVICE="${DISK}${ESP_PART}"
export SWAP_DEVICE="${DISK}${SWAP_PART}"
export POOL_DEVICE="${DISK}${POOL_PART}"
echo $ESP_DEVICE && echo $SWAP_DEVICE && echo $POOL_DEVICENVMe
Example disk: nvme0n1
export DISK="/dev/nvme0n1"
export ESP_PART="1"
export SWAP_PART="2"
export POOL_PART="3"
export ESP_DEVICE="${DISK}p${ESP_PART}"
export SWAP_DEVICE="${DISK}p${SWAP_PART}"
export POOL_DEVICE="${DISK}p${POOL_PART}"
echo $ESP_DEVICE && echo $SWAP_DEVICE && echo $POOL_DEVICEWipe the DISK
If there was previously a ZFS pool on DISK, run:
zpool labelclear -f $DISK
If DISK was previously configured with LVM, bring down the volume group:
vgchange -an
Wipe existing file systems and partition table on DISK:
wipefs -af $DISK && sgdisk --zap-all --clear $DISK
Notify the system of changes to the partition table:
partprobe $DISKPartition the DISK
NOTE
Many partitioning guides assign 256-512M of space to the EFI system partition. I like to future-proof the partition for whatever else Linux might want to store there by assigning a more generous 4G of space.
Create a GPT partition table on DISK with the following layout:
| Number | Size | Code | Format | Use as | Mountpoint |
|---|---|---|---|---|---|
| 1 | 4g | ef00 | vfat | EFI system partition | /efi |
| 2 | 8g | 8200 | swap | Swap partition | (not applicable) |
| 3 | ->END | bf00 | zfs | ZFS pool partition | / |
Create the EFI system partition:
sgdisk -n "${ESP_PART}:1m:+4g" -t "${ESP_PART}:ef00" -c 0:esp $DISK
Create the swap partition:
sgdisk -n "${SWAP_PART}:0:+8g" -t "${SWAP_PART}:8200" -c 0:swap $DISK
Create the ZFS pool partition:
sgdisk -n "${POOL_PART}:0:0" -t "${POOL_PART}:bf00" -c 0:pool $DISK
Display DISK layout:
partprobe $DISK && sgdisk -p $DISKFormat the ESP partition
NOTE
Labels on file systems are optional, but helpful. They are a more reliable way to identify the correct partition than simple device nodes and allow for easy mounting without a UUID.
Create a fat32 file system on the partition:
mkfs.fat -n ESP -F 32 $ESP_DEVICE03. Create the ZFS Pool
When adding disks or partitions to ZFS pools, it is advisable to refer to them by the symbolic links created in /dev/disk/by-partuuid so that ZFS will identify the right devices even if disk naming should change at some point. Using device nodes like /dev/sda2 may cause import failures.
So I create a POOL_ID variable:
POOL_ID=/dev/disk/by-partuuid/$( blkid -s PARTUUID -o value $POOL_DEVICE )
Verify:
ls -al /dev/disk/by-partuuid/ && echo "POOL_ID = $POOL_ID"Create the encryption keyfile
Store the encryption passphrase for the ZFS pool in a keyfile:
echo 'SuperSecretPassphrase' > /etc/zfs/zroot.key
chmod 000 /etc/zfs/zroot.keyCreate the encrypted ZFS pool
Create the pool with native encryption enabled:
zpool create -f \
-o ashift=12 \
-o autotrim=on \
-o compatibility=openzfs-2.3-linux \
-O acltype=posixacl \
-O xattr=sa \
-O compression=lz4 \
-O encryption=aes-256-gcm \
-O keylocation=file:///etc/zfs/zroot.key \
-O keyformat=passphrase \
-O relatime=on \
-m none zroot "$POOL_ID"Define ID variable
File /etc/os-release defines variables that describe the current operating system. Use the $ID variable to set the short name of the Linux distribution in later commands:
. /etc/os-release && export ID && env | grep IDCreate the ZFS datasets
NOTE
It is necessary to explicitly set the canmount=noauto on every boot environment you create.
zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/${ID}
Set the preferred boot file system:
zpool set bootfs=zroot/ROOT/${ID} zroot
I create an additional home dataset for each system:
zfs create zroot/ROOT/${ID}/home
This allows me to keep user config files unique to each boot environment that I might create in the future. It also separates user data from system data, which is useful for ZFS snapshots and enables system rollbacks while leaving user data untouched.
To share data between boot environments, I create a data dataset to store files common to all of them:
zfs create -o mountpoint=/data zroot/dataExport and re-import pool for installation
zpool export zroot
zpool import -N -R /mnt/gentoo zroot
zfs load-key -L prompt zrootMount the ZFS datasets
zfs mount zroot/ROOT/${ID}
zfs mount zroot/ROOT/${ID}/home
zfs mount zroot/data
Verify:
# mount -t zfs
zroot/ROOT/gentoo on /mnt/gentoo type zfs (rw,relatime,xattr,posixacl,casesensitive)
zroot/ROOT/gentoo/home on /mnt/gentoo/home type zfs (rw,relatime,xattr,posixacl,casesensitive)
zroot/data on /mnt/gentoo/data type zfs (rw,relatime,xattr,posixacl,casesensitive)
Update device symlinks:
udevadm trigger04. Install the Gentoo Base System
The stage file acts as the seed of a Gentoo install. There are many versions of stage files based on specific profiles (the characteristics of the system being set up), and contain an almost-complete system.
Before downloading the stage file, the current directory should be set to the location of the mount used for the install:
cd /mnt/gentooInstall the stage file
Download stage tarballs from amd64/stages.
For the purposes of this HOWTO, I chose desktop profile | openrc and downloaded:
stage3-amd64-desktop-openrc*.tar.xztarballstage3-amd64-desktop-openrc*.tar.xz.ascfile
Verify the stage file:
gpg --import /usr/share/openpgp-keys/gentoo-release.asc
gpg --verify stage3-amd64-desktop-openrc*.tar.xz.asc stage3-amd64-desktop-openrc*.tar.xz
Once verified, it can be extracted using tar:
tar xpvf stage3-amd64-desktop-openrc*.tar.xz --xattrs-include='*.*' \
--numeric-owner -C /mnt/gentooConfigure compile options
Modify the make.conf file to configure system-wide compile options for software:
nano /mnt/gentoo/etc/portage/make.confCFLAGS and CXXFLAGS
COMMON_FLAGS="-march=native -O2 -pipe"RUSTFLAGS
RUSTFLAGS="${RUSTFLAGS} -C target-cpu=native"MAKEOPTS
Print the number of processing units available:
nproc
Set:
- MAKEOPTS
-jjobs value to the same number of threads returned bynproc - MAKEOPTS
-lload-average value tonproc + 1
MAKEOPTS="-j[nproc] -l[nproc + 1]"
More: MAKEOPTS
USE flags
USE="elogind -systemd"
Save changes and exit.
Generate hostid
Generate hostid hexadecimal identifier for use by ZFSBootMenu (0x00bab10c is the default value used internally by ZBM):
zgenhostid -f 0x00bab10c && hostidEnter chroot
Copy files into the mounted ZFS file system:
cp /etc/hostid /mnt/gentoo/etc/
cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
mkdir /mnt/gentoo/etc/zfs && cp /etc/zfs/zroot.key /mnt/gentoo/etc/zfs/
Mount additional file systems for the chroot:
mount --types proc /proc /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys
mount --make-rslave /mnt/gentoo/sys
mount --rbind /dev /mnt/gentoo/dev
mount --make-rslave /mnt/gentoo/dev
mount --bind /run /mnt/gentoo/run
mount --make-slave /mnt/gentoo/run
Enter the chroot environment:
chroot /mnt/gentoo /bin/bash
. /etc/profile && export PS1="(chroot) ${PS1}"Configure portage
Fetch the latest portage snapshot (which is released on a daily basis) from a Gentoo mirror:
emerge-webrsync
To read any news items that might appear:
eselect news list
eselect news readSelect profile
View the available system profiles and the currently active profile:
eselect profile list
Stick with the active profile or, if desired, select a different one:
eselect profile set [number]
Example: I chose [3] default/linux/amd64/23.0/desktop (stable):
eselect profile set 3
More: Choosing the right profile
Add binary package host
Though Gentoo is famous for its toolkit for compiling source code, most available software has a corresponding binary package for quick 'n easy installation if compilation is not required.
Check if my target device supports x86-64-v3 (newer CPU instruction set extensions):
# ld.so --help
.
.
.
Subdirectories of glibc-hwcaps directories, in priority order:
x86-64-v4 (supported, searched)
x86-64-v3 (supported, searched)
x86-64-v2 (supported, searched)
It does.
For this example configuration, I'm going to use the mirror.csclub.uwaterloo.ca used earlier for the ISO image download, but now as a source of binary packages.
Modify the gentoo.conf file:
nano /etc/portage/binrepos.conf/gentoo.conf
Modify/add entries:
[gentoo]
priority = 9990
sync-uri = https://mirror.csclub.uwaterloo.ca/gentoo-distfiles/releases/amd64/binpackages/23.0/x86-64
location = /var/cache/binhost/gentoo
verify-signature = true
[gentoo-x86-64-v3]
priority = 9999
sync-uri = https://mirror.csclub.uwaterloo.ca/gentoo-distfiles/releases/amd64/binpackages/23.0/x86-64-v3/
location = /var/cache/binhost/gentoo-x86-64-v3
verify-signature = true
Save changes and exit.
I also configure the Portage package manager to use binary packages by default:
echo \
'FEATURES="${FEATURES} getbinpkg binpkg-request-signature"' >> /etc/portage/make.conf
Run getuto for Portage to set up the necessary keyring for verification:
getuto
More: Gentoo Binary Host Quickstart
Update system
Update the ebuild repository:
emerge --sync
Update @world:
emerge --ask --verbose --update --deep --changed-use @world
Set the ACCEPT_LICENSE variable:
echo 'ACCEPT_LICENSE="-* @FREE @BINARY-REDISTRIBUTABLE"' >> /etc/portage/make.confConfigure locales
Open the /etc/locale.gen file:
nano /etc/locale.gen
Uncomment the desired system locales (example: en_CA and en_US), then save changes and exit.
Generate the locales:
locale-gen
With eselect locale list, the available targets are displayed:
# eselect locale list
Available targets for the LANG variable:
[1] C
[2] C.UTF-8 *
[3] POSIX
[4] en_CA.UTF-8
[5] en_US.UTF-8
[ ] (free form)
Select the correct [NUM] from the list to set the correct locale:
eselect locale set [NUM]
Reload the environment:
env-update && . /etc/profile && export PS1="(chroot) ${PS1}"05. Configure the Linux Kernel
Install firmware and microcode
Linux firmware:
emerge -av sys-kernel/linux-firmware
Sound Open Firmware (SOF):
emerge -av sys-firmware/sof-firmware
Microcode updates for AMD CPUs are included in the sys-kernel/linux-firmware package. Microcode for Intel CPUs can be found within the sys-firmware/intel-microcode package:
emerge -av sys-firmware/intel-microcode
More: Microcode
Installkernel
Installkernel may be used to automate the kernel installation and initramfs generation:
emerge -av sys-kernel/installkernelInstall the distribution kernel
To trigger an automatic rebuild for external kernel modules (such as the zfs modules) and re-generation of the initramfs, open the make.conf file:
nano /etc/portage/make.conf
Enable the dist-kernel and dracut flags by adding them to USE=:
USE="dist-kernel dracut"
Save changes and exit.
Install the kernel:
emerge -av sys-kernel/gentoo-kernel-bin
Once installed, Portage will automatically update the kernel to newer versions. The previous versions will be kept until the package manager is directed to clean up stale packages.
Install ZFS file system tools
emerge --ask sys-fs/zfsGenerate the initramfs
NOTE
Generating an initramfs is essential for a Root-On-ZFS system, as the zfs module must be loaded before the kernel is launched.
Dracut is used to generate the initramfs. Create a configuration directory to store our config file:
mkdir /etc/dracut.conf.d
Enable ZFS support by creating the zfs.conf file:
nano /etc/dracut.conf.d/zfs.conf
Add these settings:
nofsck="yes"
add_dracutmodules+=" zfs "
omit_dracutmodules+=" btrfs resume "
force_drivers+=" zfs "
install_items+=" /etc/zfs/zroot.key "
NOTE
The spaces inside the quotes are required.
Save changes and exit.
Rebuild the initramfs for the distribution kernel:
emerge --config sys-kernel/gentoo-kernel-bin06. Configure the System
Set the root password
passwdCreate a superuser
Create a user account with superuser privileges:
useradd -m -G wheel -s /bin/bash [username]
...where [username] is the desired name for the account.
Set a password for [username]:
passwd [username]Assign the hostname
Create the hostname file:
echo [hostname] > /etc/hostname
...where [hostname] is the desired name of the system (single word, no spaces):
echo gentoofoo > /etc/hostnameSet the console font
I like a large, crisp font on the console, and Terminus is my font of choice:
emerge -av media-fonts/terminus-font
For most modern setups using UTF-8, list the different sizes and weights (bold, normal) in the ter-v* (Uni3 encoding) series:
ls /usr/share/consolefonts/ter-v*
Test a font (minus the .psf.gz) in the current session (example: ter-v18b):
setfont ter-v18b
If it looks good, that's the name to save.
To make the font persistent across reboots, open the consolefont file:
nano /etc/conf.d/consolefont
Set:
consolefont="ter-v18b"
Save changes and exit.
Enable the service:
rc-update add consolefont boot
More: Fonts/Console
Set the console keymap
Default keymap is us.
If a keymap alternative is desired, view the available keymaps in /usr/share/keymaps.
Example: I like the colemak keymap layout (available in /usr/share/keymaps/i386/colemak/), which I set by modifying the keymaps file:
nano /etc/conf.d/keymaps
Set:
keymap="en-latin9"
Save changes and exit.
More: Keyboard layout switching
Set the timezone
Timezones are located in /usr/share/zoneinfo/[Region]/[City], where [Region] is the geographical region (Africa, America, Europe, ...) and the [City] within that region.
Example: Create the /etc/localtime symbolic link to the timezone where Toronto is located:
ln -sf /usr/share/zoneinfo/America/Toronto /etc/localtime && dateEnable zfs services
The zfs-import and zfs-mount services must be enabled for the system to boot:
rc-update add zfs-import sysinit
rc-update add zfs-mount sysinitAdd to fstab
Add the ESP partition and configure /tmp to be mounted in RAM for a speed boost:
echo 'LABEL=ESP /efi vfat defaults 0 0' >> /etc/fstab
echo 'tmpfs /tmp tmpfs rw,nosuid,nodev,size=8G,mode=1777 0 0' >> /etc/fstabCreate encrypted swap
Using raw dm-crypt enables the system to generate a random, one-time encryption key at boot that requires no action from the user to encrypt swap. At shutdown the key is discarded, rendering any remaining data effectively destroyed.
Install:
emerge -av sys-fs/cryptsetup
Retrieve the PARTUUID of SWAP_DEVICE:
blkid | grep $SWAP_DEVICE
Open the dmcrypt file:
nano /etc/conf.d/dmcrypt
Add the following configuration block to the bottom of the file, replacing [partuuid_of_swap_device] with the string of characters returned by blkid:
target=swap
source='/dev/disk/by-partuuid/[partuuid_of_swap_device]'
key='/dev/urandom'
options='-c aes-xts-plain64 -s 512'
pre_mount='mkswap /dev/mapper/swap'
Save changes and exit.
Add swap device to fstab:
echo '/dev/mapper/swap none swap defaults,nofail 0 0' >> /etc/fstab
Enable the service:
rc-update add dmcrypt boot07. System Tools
Login manager: elogind
Install:
emerge -av sys-apps/dbus sys-auth/elogind
Enable the service:
rc-update add elogind boot
When dbus is installed with the USE="elogind" flag, starting elogind on boot triggers the dbus system daemon to load automatically.
Network: dhcpcd
Install:
emerge -av net-misc/dhcpcd
Enable the service:
rc-update add dhcpcd default Root privileges: sudo
Install:
emerge -av app-admin/sudo
Activate wheel group access for the sudo command:
sed -i "s/# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/" /etc/sudoersSystem logger: sysklogd
Install:
emerge -av app-admin/sysklogd
Enable the service:
rc-update add sysklogd defaultCron daemon: cronie
Install:
emerge -av sys-process/cronie
Enable the service:
rc-update add cronie defaultRemote access: sshd
Enable the service:
rc-update add sshd defaultNTP: chrony
Install:
emerge -av net-misc/chrony
Enable the service and time will be synchronized automatically at boot:
rc-update add chronyd default08. ZFSBootMenu
Install the ZFSBootMenu (ZBM) bootloader to support Root-on-ZFS boot environments on Linux.
Set boot properties
NOTE
Add the hibernate=no argument to ensure that no process can trigger a suspend-to-disk action, which is consistent with the goal of using an ephemeral, random-key swap space.
Assign command-line arguments to be used when booting the kernel:
zfs set org.zfsbootmenu:commandline="quiet hibernate=no" zroot/ROOT
Configure key caching:
zfs set org.zfsbootmenu:keysource="zroot/ROOT/${ID}" zrootMount the ESP partition
mkdir /efi
mount $ESP_DEVICE /efiInstall prebuilt executable
Install a prebuilt ZBM executable to the ESP:
mkdir -p /efi/EFI/ZBM
wget -L https://get.zfsbootmenu.org/efi -O /efi/EFI/ZBM/VMLINUZ.EFI
cp /efi/EFI/ZBM/VMLINUZ.EFI /efi/EFI/ZBM/VMLINUZ-BACKUP.EFIConfigure EFI boot entries
Install:
emerge -av sys-boot/efibootmgr
Configure boot entries for the ZBM executable:
efibootmgr -c -d "$DISK" -p "$ESP_PART" -L "ZFSBootMenu (Backup)" \
-l '\EFI\ZBM\VMLINUZ-BACKUP.EFI'
efibootmgr -c -d "$DISK" -p "$ESP_PART" -L "ZFSBootMenu" \
-l '\EFI\ZBM\VMLINUZ.EFI'09. Finish Up
Exit, unmount, and export
Exit chroot:
exit
cd
Unmount everything:
umount -l /mnt/gentoo/dev{/shm,/pts,} && umount -l -n -R /mnt/gentoo
Export the zpool:
zpool export zrootReboot
reboot
NOTE
When prompted for the passphrase to unlock zpool, keymap is us regardless of the keymap that might have been set in /etc/conf.d/keymaps.
User is prompted for the passphrase to unlock the encrypted root partition. Upon success, boot resumes...
gentoofoo login:
Welcome to Gentoo!
10. Resources
- Gentoo Wiki: AMD64 Handbook and ZFS/rootfs
- OpenZFS Man Pages: zpoolprops.7 and zfsprops.7
- Installation Guides: ZFSBootMenu
- Practical ZFS: Linux home directory on ZFS
- Why Gentoo?
Next: Gentoo Linux: After the First Boot (TODO)
You can like, share, or comment on this post on the Fediverse 💬
« Previous: Make Your Own Fortune