Switching from Arch to Gentoo
Status: Completed

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

Why?

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 dependencies issues after each pacman -S ? made me rethink my life choices. So I decided to try Gentoo.

This is not for Linux beginners. You need at least the skill to install Arch without relying on any script or tool, in addition to some Linux scripting.

Installation Medium

Download the desired ISO and dd to the USB key (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 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

Partitions setup

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

  • Use fdisk tool to clear and create a new partition
fdisk /dev/nvme0n1
# p: print table, d: delete partition, g to clear it all.

Simple setup

partition size goal fdisk sequence filesystem
/dev/nvme0n1p1 ~ 512MB EFI boot n -> Enter x 2 -> +512M -> t -> 1 for EFI vfat
/dev/nvme0n1p2 ~ 32GB swap 19 for swap swap
/dev/nvme0n1p3 ~ 320GB linux root no need to specify the type here ext4
/dev/nvme0n1p4 ~ ?GB linux home no need to specify the type here ext4

Using RAID-1

There are 2 types of hard drives:

  • Drives that have failed
  • Drives that have not failed yet

I've learned it the hard way, and as Moroccans say: The one bitten by a snake becomes scared of ropes.

We can set up redundant array based on software RAID-1 for the root and home partitions: both hard 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 RAID-1 module: modprobe raid1. (Use lsmod to see loaded kernel modules)
  • Make block device nodes with major 9 and minor 1-3.
# pick your major/minor numbers
mknod /dev/md1 b 9 1 # /root
mknod /dev/md2 b 9 2 # /home

Before we proceed with mdadm, create the partition table as above but using Linux Raid partition type (42 on GPT) for / and /home

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

mdadm will start the parity sync in the background, to track:

watch -n 1 cat /proc/mdstat

Once done, save the raid config :

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

Formats and mount paths

To avoid any confusion between non-RAID/RAID, we'll use the conventions:

- `/dev/sda1` for the boot partition
- `/dev/sda2` for the root partition
- `/dev/sda3` for the home partition
- `/dev/sda4` for the swap, if applicable
  • Apply the file-system
mkfs.vfat -F 32 /dev/sda1
mkfs.ext4 /dev/sda2
mkfs.ext4 /dev/sda3
mkswap /dev/sda4
swapon /dev/sda4 # start swapping, used when RAM is exhausted
  • Let's mount / and /home:
mkdir -p /mnt/gentoo # -p to create parents if needed
mount /dev/sda2 /mnt/gentoo/
mkdir /mnt/gentoo/home
mount /dev/sda3 /mnt/gentoo/home

Output of 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

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

  • Start by setting up the time, assuming we got network access
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
  • Select mirrors
mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf
  • Create the ebuild repository config
cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf
  • Copy DNS settings
cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
  • Bind Linux directories to the new root: these contain nodes of the processes and devices
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
  • If using RAID: cp /tmp/mdadm.conf /mnt/gentoo/etc

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

Gentoo's Package Manager: Portage

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:

  • 0-? USE flags to configure the compilation,
  • a slot: in case we need different versions of the same package, they should use different slots,
  • keywords: the architecture of the ebuild

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

To manipulate USE flags:

  • Set global USE flags via /etc/portage/make.conf: USE="-gtk sensors" to disable gtk and enable sensors for all packages.
  • Set per-package USE flags via /etc/portage/package.use/*.conf.

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).

  • Select a profile using eselect profile list. I'll go for the default (OpenRC) KDE Plasma
# default/linux/...desktop/plasma
eselect profile set 9
  • You can see your default profile using eselect profile show. It's a symlink to your choice from the profiles database located at /var/db/repos/gentoo/profiles/ and its files (plasma) at subfolder targets/desktop/plasma. These are not to be edited.
  • Use portageq envvar USE to see the current list of use flags

  • Open /mnt/gentoo/etc/portage/make.conf and add -march=native to COMMON_FLAGS. This is telling portage to use the native architecture.

  • Add some optimization flags (recommended) as CPU_FLAGS_X86 using cpuid2cpluflags. Before you go for the first merge, you might want to add some USE options to avoid re-merging.
#/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"
  • Let's start by installing all the system packages of this set, it might take a while... Welcome to Gentoo!
# first emerge
emerge --ask --verbose --update --deep --newuse @world
  • Set locales:
# list timezones : ls /usr/share/zoneinfo
echo "Europe/Paris" > /etc/timezone
rm /etc/localtime
merge --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}"

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. It seemed like a sign for me to continue down this road:

"[...] even after a long time, you will never quite stop tearing your hairs out and pleading to the elders of Portage to resolve your slot conflicts during an update. But you will persevere, as each problem will reveal more of the system and the power you gain from it is unmatched by any other. Soon you'll realize that all this complexity behind Gentoo, the all-tangled mess of dependencies, conflicting versions and configurations, this is what modern software is and that it's simply been hidden away from you.

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.

Booting Gentoo

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

  • Dump all the UUIDs to fstab : blkid > /etc/fstab
  • Clean and adjust the mounts:
# <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
  • Install GRUB (sys-boot/grub) after adding GRUB_PLATFORMS to make.conf (see above) to ensure that it's compiled with EFI support.
  • If RAID: add domdadm to GRUB_CMDLINE_LINUX in /etc/default/grub.
grub-install --target=x86_64-efi --efi-directory=/boot
  • Grub menu should be generated after the kernel installation (see next), and is done via:
grub-mkconfig -o /boot/grub/grub.cfg

Kernel

The RAID partitions need a custom config (skip to the serious way)

The easy way

Using a generic kernel configuration

# 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

This will also provide a generic kernel configuration saved in /boot/config-*-gentoo-dist which we can start from for any subsequent customization

The serious way

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

We'll be using genkernel tool to manage the compilation of the kernel. I wrote the post here: [INSERT:genkernel]

The additional settings we need are:

  • In /etc/genkernel.conf
  • MDADM="yes"
  • MDADM_CONFIG="/etc/mdadm.conf"
  • Start the configuration
genkernel --kernel-config=/boot/config-* \
          --kernel-append-localversion="tutorial" \
          --install all

Use '/' to search and enable CONFIG_MD_RAID1. It should take care of updating grub menu configuration. 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 you 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...)

To explore: Check Gentoo's guides

Portage dependency cycle

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/\*.conf

Portage tricks

  • Gentoo has its own "AUR" equivalent. You can also create your own repository of ebuilds.
  • Learn about portage "sets". It's helpful to keep track of world set for the packages that you've installed (excludes dependencies)
  • You can back up /etc/ to a GitHub repo
  • If you have a home server, you can set it up to compile packages for you to save time on your working PC.