Just Enough Gentoo

Last edited on 2026-06-16 Tagged under  #gentoo   #linux   #zfs   #encrypt   #zbm 

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.

ascii kitty

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 esp serves as the EFI system partition and is formatted with the fat32 file system.
  • Partition swap provides swap space with encryption courtesy of dm-crypt.
  • Partition pool contains root and is formatted with the zfs file system using native encryption.

A few assumptions:

  • Target device is amd64 architecture using UEFI to boot.
  • Use glibc as 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.
  • OpenRC will be used as system init and ZFSBootMenu as 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].iso
  • install-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.sha256

Prepare 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/sdx

Boot 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.org

Remote 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 && date

02. 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_DEVICE

NVMe

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_DEVICE

Wipe 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 $DISK

Partition 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:

NumberSizeCodeFormatUse asMountpoint
14gef00vfatEFI system partition/efi
28g8200swapSwap partition(not applicable)
3->ENDbf00zfsZFS 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 $DISK

Format 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_DEVICE

03. 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.key

Create 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 ID

Create 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/data

Export and re-import pool for installation

zpool export zroot
zpool import -N -R /mnt/gentoo zroot
zfs load-key -L prompt zroot

Mount 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 trigger

04. 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/gentoo

Install 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.xz tarball
  • stage3-amd64-desktop-openrc*.tar.xz.asc file

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/gentoo

Configure compile options

Modify the make.conf file to configure system-wide compile options for software:

nano /mnt/gentoo/etc/portage/make.conf

CFLAGS 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 -j jobs value to the same number of threads returned by nproc
  • MAKEOPTS -l load-average value to nproc + 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 && hostid

Enter 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 read

Select 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.conf

Configure 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/installkernel

Install 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/zfs

Generate 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-bin

06. Configure the System

Set the root password

passwd

Create 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/hostname

Set 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 && date

Enable 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 sysinit

Add 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/fstab

Create 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 boot

07. 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/sudoers

System logger: sysklogd

Install:

emerge -av app-admin/sysklogd

Enable the service:

rc-update add sysklogd default

Cron daemon: cronie

Install:

emerge -av sys-process/cronie

Enable the service:

rc-update add cronie default

Remote access: sshd

Enable the service:

rc-update add sshd default

NTP: chrony

Install:

emerge -av net-misc/chrony

Enable the service and time will be synchronized automatically at boot:

rc-update add chronyd default

08. 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}" zroot

Mount the ESP partition

mkdir /efi
mount $ESP_DEVICE /efi

Install 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.EFI

Configure 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 zroot

Reboot

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

Next: Gentoo Linux: After the First Boot (TODO)

You can like, share, or comment on this post on the Fediverse 💬

Thanks for reading! Read other posts?

« Previous: Make Your Own Fortune