Skip to article frontmatterSkip to article content

This is a guide for future reference, written as I was installing Gentoo to keep track of the different steps.

After years of using Ubuntu (2014‑2018), my C/C++ up‑bringing has forced me to seek a more minimal and optimized distro. I’ve switched to Arch and never looked back since. The package manager and the dependency issues after each pacman -S ? made me rethink my life choices. So I decided to try Gentoo.

Preparation & Installation Medium

Download the desired ISO and dd it to the USB key (use lsblk to find the device):

# /dev/sdc not /dev/sdc1
dd if=install-amd64-minimal-{some date}.iso of=/dev/sdc bs=8192k

I recommend the LiveGUI edition to avoid a frustrating start with Wi‑Fi/Network setup. If you still prefer the minimal installation medium, you can set up Wi‑Fi as follows:

# get the wifi device (wlp170s0 in my case)
ifconfig
# activate the interface
ip link set dev wlp170s0 up
echo ctrl_interface=/run/wpa_supplicant >> /etc/wpa_supplicant/wifi.conf
echo update_config=1  >> /etc/wpa_supplicant/wifi.conf
wpa_passphrase your_ssid your_password >> /etc/wpa_supplicant/wifi.conf
# connect using the creds
wpa_supplicant -B -i wlp170s0 -c /etc/wpa_supplicant/wifi.conf
# simple ping to check
ping google.com

Disk Partitioning

To display the current partitions you can use fdisk -l or lsblk. My drive is /dev/nvme0n1.

Use fdisk to clear and create a new partition table:

fdisk /dev/nvme0n1
# p: print table, d: delete partition, g to clear it all.

Simple setup

partitionsizegoalfdisk sequencefilesystem
/dev/nvme0n1p1~ 512 MBEFI bootn → Enter x 2 → +512M → t → 1 for EFIvfat
/dev/nvme0n1p2~ 32 GBswap19 for swapswap
/dev/nvme0n1p3~ 320 GBlinux root– (type is implied)ext4
/dev/nvme0n1p4~ ? GBlinux home– (type is implied)ext4

Using RAID‑1

There are 2 types of hard drives:

As the saying goes in Morocco: The person who gets bitten by a snake becomes scared of ropes. So now we need a safety hack for drive failures.

I set up redundant array based on software RAID-1 for the root and home partitions: both drives (nvme0,nvme1) would contain a partition pair (same size), and we need to create virtual partitions that will mirror the redundant pair as one.

Load the RAID‑1 module and create block‑device nodes:

modprobe raid1          # load module
mknod /dev/md1 b 9 1    # /root
mknod /dev/md2 b 9 2    # /home

Create matching partitions on both disks (Linux RAID type – GPT code 42) and assemble them with mdadm:

# metadata: 0.90 (old kernels) or 1.20 (recommended)
mdadm --create --verbose --level=1 --metadata=1.2 --raid-devices=2 \
    --name=root --homehost=ghriss /dev/md1 /dev/nvme0n1p3 /dev/nvme1n1p3
mdadm --create --verbose --level=1 --metadata=1.2 --raid-devices=2 \
    --name=home --homehost=ghriss /dev/md2 /dev/nvme0n1p4 /dev/nvme1n1p4

Watch the parity sync:

watch -n 1 cat /proc/mdstat

When finished, save the RAID configuration for later use:

mdadm --detail --scan >> /tmp/mdadm.conf

Formats and mount paths

Use the following naming convention to keep things clear (non‑RAID or RAID):

DeviceMount pointFilesystem
/dev/sda1/bootvfat
/dev/sda2/ext4
/dev/sda3/homeext4
/dev/sda4none (swap)swap

Apply the filesystems:

mkfs.vfat -F 32 /dev/sda1
mkfs.ext4   /dev/sda2
mkfs.ext4   /dev/sda3
mkswap      /dev/sda4
swapon      /dev/sda4   # enable swapping

Mount the root and home filesystems:

mkdir -p /mnt/gentoo          # create target tree
mount /dev/sda2 /mnt/gentoo/
mkdir /mnt/gentoo/home
mount /dev/sda3 /mnt/gentoo/home

Verify the layout with lsblk:

NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0         7:0    0 423.5M  1 loop /mnt/livecd
...
sda     259:0    0 953.9G  0 disk
├─sda1 259:1    0   512M  0 part
├─sda2 259:2    0   320G  0 part /mnt/gentoo
├─sda3 259:3    0   580G  0 part /mnt/gentoo/home
└─sda4 259:4    0    32G  0 part [SWAP]

Preparing Gentoo Root

Now we’re ready to get all the necessary base files to start the journey.

chronyd -q
# display time
date

We move to the (future) root directory and choose a stage tarball. I went with Stage 3 Desktop-OpenRC.

cd /mnt/gentoo
# terminal browser, go to downloads
links https://www.gentoo.org/downloads
# file: stage3-amd64-desktop-openrc...tar.xz
# unpack while preserving namespaces and ownerships
tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner
mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf
cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf
cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
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

Chrooting

Change the root directory to /mnt/gentoo:

chroot /mnt/gentoo /bin/bash
source /etc/profile
# add chroot to terminal to distinguish non-chroot
export PS1="(chroot) ${PS1}"
# Mount the boot partition
mount /dev/sda1 /boot

TMPFS (optional)

I’ve noticed that my failed drive had #writes = 3 x #reads. Probably due the swapping and caching. For the caching, we can use RAM via tmpfs to reduce IO on SSD and accelerate compilation:

mount -t tmpfs tmpfs /tmp
# /var/tmp/portage used by package manager for caching and build files
mount -t tmpfs tmpfs /var/tmp

Portage - Gentoo’s Package Manager

Here is how I understand it: instead of providing binaries or pre-packaged files, Portage deals in ebuilds, which are compilation scripts of different packages. Each ebuild (package) has:

To merge a package (install) you use emerge $PACKAGE. Each package belongs to a category that is useful to distinguish policy/configuration packages. We can see the use flags of a package via equery uses grub (included in gentoolkit), some are enabled by default (++ vs --).

To manipulate USE flags:

Example: to compile a minimal libsndfile and disabled pulseaudio for mpg123:

#/etc/portage/package.use/media.conf
media-libs/libsndfile minimal
media-sound/mpg123 -pulseaudio

We start by refreshing the ebuild repo. This will download the ebuild into categories listed in /var/db/repos/gentoo:

# update ebuild repository
emerge --sync

Gentoo has a news medium to push critical messages. Use eselect news to manage it (list, then read 1 | less). After syncing the repo the previous command you might get news items need reading. I’m already loving this, it feels like Agent 47 getting new intel. It’s time to pick a profile (mission).

Portage configuration

The profile is the foundation of Gentoo’s USE flags, it sets the base USE and CFLAGS/CXXFLAGS (C/C++ compilation settings).

# default/linux/...desktop/plasma
eselect profile set 9
#/etc/portage/make.conf
CPU_FLAGS_X86="...."
# set for your video card
VIDEO_CARDS="intel"
# Used by grub later
GRUB_PLATFORMS="efi-64"
# Accept all licenses.
ACCEPT_LICENSE="*"
# no pipewire or gnome stuff, these options are cummulative
USE="-gnome -gtk -systemd -pipewire bluetooth context jack lm-sensors networkmanager python sddm"
# languages
L10N="ar en fr ja ko la zh zh-CN zh-TW"
# first emerge
emerge --ask --verbose --update --deep --newuse @world

Locales and Time

Set locales:

# list timezones : ls /usr/share/zoneinfo
echo "Europe/Paris" > /etc/timezone
rm /etc/localtime
emerge --config sys-libs/timezone-data
# configure locale
nano -w /etc/locale.gen
locale-gen
# choose system wide locale
eselect locale list
eselect set 6
env-update && source /etc/profile && export PS1="(chroot) ${PS1}"

Pause

This took a while. It’s quite stressful to go down a long path with no assurance of making it. I was rethinking my life choices when I’ve randomly stumbled upon the following comment on Reddit:

And with your skills forged by the flames, once you return to beginner pastures, you will find yourself confused by their simplicity. You will seek to dive into the guts of their tools and find all possible knobs and dials to adjust, simply because you are now able to guess how the system works from first sight alone, and you want to make sure that it works as predictably as you anticipate. And realizing how little control over your environment you have now, you may seek to return to the darkness once more.

Kernel

We need the source code of a kernel, we can use the official gentoo-sources package or any other repository. Gentoo will look for the sources at /usr/src/linux-?. We use eselect kernel to choose where the symlink /usr/src/linux points to, which will be used by default for the compilation.

# Install sources
emerge --ask sys-kernel/gentoo-sources
# Or alternative sources (e.g., Intel LTS)
# ...

# Select kernel
eselect kernel list
# Available kernel symlink targets:
#   [1]   linux-6.1.28-gentoo *
#   [2]   linux-intel-lts

The Easy Way: Distribution Kernel

Using a generic kernel configuration. This provides a generic kernel configuration saved in /boot/config-*-gentoo-dist which we can start from for any subsequent customization.

# you need to add the corresponding license in /etc/portage/package.license
emerge --ask sys-kernel/linux-firmware
# install distribution kernel
emerge --ask sys-kernel/gentoo-kernel

In case we need to rebuild the initramfs:

# rebuild initramfs
emerge --ask @module-rebuild
# or reload specific modules
emerge --config sys-kernel/gentoo-kernel

The Serious Way: Genkernel or Manual

This is necessary to include RAID-1 in the kernel and not as a loadable module if initramfs is not used. Why?: because loadable modules are saved in the root partition and we need RAID to access the root... duh!

Genkernel

We’ll be using genkernel tool to manage the compilation of the kernel.

Configuring genkernel The default genkernel config is at /etc/genkernel.conf.

Using genkernel Use ‘/’ to search and enable CONFIG_MD_RAID1 inside the menu if using RAID.

genkernel --kernel-config=/boot/config-* \
          --kernel-append-localversion="tutorial" \
          --install all

Use-case: PulseAudio I want to add the settings for Pulseaudio, starting with the current kernel’s config.

genkernel --kernel-config=/boot/config-6.1.28-gentoo-framework11 \
          --kernel-append-localversion="framework-pa" \
          --install all

We need to enable:

Use-case: KVM To enable KVM (Kernel Virtual Machines), we’ll switch to Intel’s sources: linux-intel-lts which enable SR-IOV for the iGPU.

eselect kernel set 2

We need to enable Virtualization -> Kernel-based Virtual Machine (KVM) support, Host kernel accelerator, KVM for Intel, and the various Networking and Graphics support options detailed in the Gentoo wiki or Intel documentation.

Manual Compilation

When it comes to compiling your own kernel manually, you can make it.

# enable modules from lsmod
make localmodconfig
make menuconfig

You do not want to change .config directly. Use “/” to start a search, “H” to see the documentation of each config, and check “DEPENDS”.

# choose a number of workers to use
make -j8
# install modules
make -j8 modules_install
# install kernel
make install

Booting Gentoo

We’ll be using Grub 2. First, let’s create our fstab:

# <device>  <dir>             <type>  <options>       <dump/pass> <fsck order>
# /dev/sda1
UUID=?      /boot	        vfat	defaults,noatime	0 2
# /dev/sda2
UUID=?      /		        ext4	noatime			0 1
# /dev/sda3
UUID=? 	    /home	        ext4	noatime 		0 1
# /dev/sda4
UUID=?      none	        swap	sw			0 0
# TMPFS mounts
tmpfs	    /tmp		tmpfs	rw,nodev,nosuid	0 0
tmpfs       /var/tmp		tmpfs	rw,nodev,nosuid	0 0

# In case you dual boot windows & you want to have a shared partititon
# Make sure to disable fast startup on Windows to avoid locking the partition
UUID=?	    /media/shared	ntfs	auto,user,rw,uid=1000,guid=1000	0 1
grub-install --target=x86_64-efi --efi-directory=/boot
grub-mkconfig -o /boot/grub/grub.cfg

Networking & Finalizing

The next step is to set up networking packages before we reboot. I recommend you follow Gentoo’s official guide for that because it will depend on your configuration. For me, I use Ethernet... it’s simple:

echo ghriss > /etc/hostname
emerge --ask networkmanager

The rest of the installation is up to you (Desktop environment, display manager...).

Portage tricks

We have the following cycle libsndfile -> mpg123 -> libpulse -> libsndfile. To break the cycle we set the minimal use flag for libsndfile by adding media-libs/libsndfile minimal to /etc/portage/package.use/media.conf.