Click here to skip straight to the installation guide.

Motivation

This article is meant for those who are already familiar with other Linux distributions and the command line and would like to take a deeper dive into the inner workings of a minimalist, do-it-yourself distribution. I would not recommend going through with this installation unless you have the motivation to persist when you (inevitably) encounter an issue.

What is Gentoo?

Gentoo Linux is a source-based Linux distribution, meaning that (almost) all packages can be installed by compiling them from publicly-available source code. This gives a number of benefits, including CPU-specific optimizations, customization of binaries with USE flags, and the avoidance of binary exploitations. Our primary goal here, however, will be to gain increased knowledge of GNU/Linux systems, and have some fun while doing it.

The downsides

However, these benefits come at the expense of compile times. While on binary distributions, such as Ubuntu or Arch Linux, binaries are downloaded from centralized repositories and installed, on Gentoo, the repositories mostly store source code. This source code is downloaded and then compiled on your machine locally, a process which can take a while depending on the size of the package and the core count and clock speed of your CPU. Thus, you should only use Gentoo if the compile times will not hinder your productivity by taking up all of your time.

Installation guide

Here, I will provide a complete set of instructions for building a Gentoo system and installing it on your machine. The guide is split into a number of larger sections, each of which are numbered as follows:

Table of Contents

Click on any of the following headings to skip directly to that section.

Building a minimal system

  1. Preparing the installation medium
  2. Partitioning, dm-crypt, LVM, filesystems, and mounting
  3. Downloading the stage 3 tarball
  4. Configuring portage and chrooting
  5. Bootstrapping the system
  6. Switching to the desktop profile
  7. Configuring and compiling the kernel
  8. Miscellaneous configuration and installing tools
  9. Installing and configuring bootloader

1. Preparing the installation medium

While the official Gentoo installation handbook suggests using the Gentoo minimal live CD, we will use the Pop!_OS live CD to have access to a terminal emulator and web browser throughout the process. Download the newest Pop!_OS ISO from here and burn it to a USB drive. If you’d prefer not to use Pop!_OS for the installation, choose your favorite Ubuntu-based distribution and continue with the guide. If you don’t know how to use dd, follow these instructions from the Linux Mint team regardless of your current operating system. Note that this installation can be performed from any Linux distribution, including the Gentoo minimal CD, as long as the necessary tools are present. If you do use dd, I suggest the following command1, where /dev/sda is the USB drive:

sudo dd bs=4M if=sudo dd bs=4M if=pop-os.iso of=/dev/sda status=progress oflag=sync

Dual-booting

If you will be dual-booting with Windows 10, there are a few changes you’ll need to make. If not, continue to the next section. If your Windows partition is encrypted with Bitlocker, make sure to turn Bitlocker off before continuing. Bitlocker requires Secure Boot to be enabled while and Gentoo requires it to be disabled (without adding custom keys to the UEFI/BIOS). Next, open Disk Management and shrink your Windows partition by at least the amount you want to use for your Gentoo system. I would recommend a minimum of 100G, but of course much less can be used.

Booting the helper Linux distro

Once you have burned the USB drive, reboot your machine and enter the BIOS/UEFI settings. Turn Secure Boot off if it is on and reboot again, this time booting from the USB drive. Once Pop!_OS starts up, close the installer and connect to the internet. Open a web browser, and pull up this article again. Also open a terminal and place the two windows side-by-side. Run sudo -i to become the root user and install Neovim with apt install neovim. Note that all of the following commands should be run as the root user.


2. Partitioning, dm-crypt, LVM, filesystems, and mounting

We will now divide our hard disk into several different partitions which will serve different purposes and run different filesystems.

Creating the partitions

For our Gentoo setup, we would like two partitions: one 512MiB boot partition and an LVM partition which takes up the rest of the free space. To do so, you can use any GPT partitioning tool, but here we will use GParted. Select the unallocated space and make a new partition. Make a 512MiB primary partition with the fat32 filesystem and click Add. With the rest of the unallocated space, make a primary partition with the lvm2 pv filesystem and click Add. Click the check mark to apply these operations. Right click on the newly created boot partition, and choose Manage flags. Make sure boot and esp are checked but nothing else is.

If you would rather use GNU parted, here are the commands you should run (where <drive-name> is something like /dev/sda or /dev/nvme0n1):

parted -a optimal <drive-name>
(parted) mklabel gpt
(parted) unit mib
(parted) mkpart primary 1 513
(parted) name 1 boot
(parted) set 1 boot on
(parted) mkpart primary 513 -1
(parted) name 2 lvm
(parted) set 2 lvm on

LVM and dm-crypt

Next, we will set up logical volume management (LVM) and encrypt the LVM partition. LVM is a framework which allows for easier partition resizing among many other benefits. We will encrypt our LVM partition with dm-crypt, a subsystem of the Linux kernel which lets us encrypt drives and partitions.

We first make sure that the dm-crypt module is loaded with modprobe dm-crypt. Then, where <lvm-partition> is the partition we designated to hold the LVM volume group (e.g. /dev/sda2 or /dev/nvme0n1p2), run the following command, type YES, and enter a strong password twice:

cryptsetup -v -y -c aes-xts-plain64 -s 512 -h sha512 -i 5000 --use-random luksFormat <lvm-partition>

This command encrypts the partition, allowing us to make several logical volumes on top of it, all of which will be encrypted. Now, run the following to make sure the encryption was successful:

cryptsetup luksDump <lvm-partition>

If you see LUKS header information in the output, the encryption worked. Decrypt the crypt volume with the following, entering the password when prompted:

cryptsetup luksOpen <lvm-partition> gentoo

Now, we will create a physical volume and a volume group which will house each of our logical volumes. Create the PV and VG with:

pvcreate /dev/mapper/gentoo
vgcreate gentoo /dev/mapper/gentoo

and make sure they were created correctly with:

pvdisplay
vgdisplay

Now, we can create logical volumes, which stand in for partitions in a non-LVM setup. I suggest making a swap volume with the same amount of space as you have RAM, a root volume of 100GiB, and a home volume with the remaining space. Feel free to change these numbers to your preferences. Where <ram-amount> is your machine’s amount of RAM in GiB, run:

lvcreate -C y -L <ram-amount>G gentoo -n swap
lvcreate -L 100G gentoo -n root
lvcreate -l +100%FREE gentoo -n home

Run lvdisplay to see the exact size of the logical volumes that were just created, and run vgchange -ay to activate the volumes.

Making filesystems

On each of these logical volumes, we can make filesystems just as we would on a partition. I choose to use BTRFS on my main volumes due to its rollback features but you are welcome to use ext4 if you are more familiar with it. Simply substitute your preferred filesystem into the following commands, where <boot-partition> is the boot partition that we created earlier:

mkswap /dev/mapper/gentoo-swap
mkfs.btrfs /dev/mapper/gentoo-root
mkfs.btrfs /dev/mapper/gentoo-home
mkfs.fat -F32 <boot-partition>

Mounting partitions

Now that our partitions have been made, we will create the required mount points for them. Run the following commands to mount the root, home, and boot partitions, and to turn on the swap partition, substituting in your boot partition:

mkdir -p /mnt/gentoo
mount /dev/mapper/gentoo-root /mnt/gentoo
mkdir -p /mnt/gentoo/home
mount /dev/mapper/gentoo-home /mnt/gentoo/home
mkdir -p /mnt/gentoo/boot
mount <boot-partition> /mnt/gentoo/boot
swapon /dev/mapper/gentoo-swap

Finally, run lsblk to make sure everything is mounted correctly.


3. Downloading the stage 3 tarball

Although we have made our /root, /home, and /boot folders, as of yet they are empty. To get a minimal Gentoo filesystem, we will download a stage 3 tarball from the Gentoo website, which has certain packages already installed, including GCC, bash, and other essentials. Follow this link to download the tarball, and select the Stage 3 openrc variant. Make sure to save the tarball rather than open or unarchive it. You may choose systemd if you please, but certain commands will differ in the remainder of this tutorial.

Once the download finishes, follow this link to download the .DIGESTS and .DIGESTS.asc files for the stage 3 tarball. You should download the files stage3-amd64-<most-recent-date>.tar.xz.DIGESTS and stage3-amd64-<most-recent-date>.tar.xz.DIGESTS.asc where <most-recent-date> is the date string which corresponds to the stage 3 tarball which you just downloaded. Change directories in the terminal to where the tarball and digest files were downloaded (in Pop!_OS this is cd /home/pop-os/Downloads).

Run the following commands to compute the SHA512 and Whirlpool hashes of the tarball:

openssl dgst -r -sha512 stage3-*.tar.xz
openssl dgst -r -whirlpool stage3-*.tar.xz

Compare this output with the expected hashes by running:

cat stage3-*.tar.xz.DIGESTS | head -n4

If the hashes are the same, we can double check by verifying with the .DIGESTS.asc file. Visit the Gentoo signatures site and copy the key fingerprint labeled Gentoo Linux Release Engineering (Automated Weekly Release Key). Add this key with the following command:

gpg --keyserver pool.sks-keyservers.net --recv-keys <key-fingerprint>

where <key-fingerprint> if the fingerprint that we copied earlier. Now, the tarball can be verified with:

gpg --verify stage3-*.tar.xz.DIGESTS.asc

As long as the output says Good signature from ..., the file is verified and we can safely move on2. Move the tarball into the Gentoo /root directory, change to /root, and unarchive the tarball with:

mv stage3-*.tar.xz /mnt/gentoo/
cd /mnt/gentoo
tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner

4. Configuring portage and chrooting

Now that the minimal file structure is in place, we will configure Portage, the Gentoo package manager, before changing root (chrooting) into the Gentoo system.

Initial Portage configuration

Edit the main Portage configuration file with:

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

Change the COMMON_FLAGS variable to COMMON_FLAGS="-march=native -O2 -pipe". This means that packages that you install will compile programs only for your system, and not to be distributed to other systems. Now, take the number of threads your machine has and add one, calling this number <threads-plus-one>. Add a line to the end of the file which says MAKEOPTS=-j<threads-plus-one> and another which has EMERGE_DEFAULT_OPTS="--jobs <threads-plus-one>". These options tell Portage to use all of your CPU threads when compiling programs, and to compile programs in parallel when there are enough resources available and the particular package supports it.

Finally, add a line to specify the repository mirrors with:

GENTOO_MIRRORS="https://mirror.csclub.uwaterloo.ca/gentoo-distfiles/ https://gentoo.osuosl.org/ https://mirrors.rit.edu/gentoo/"

and save and quit. These are merely preliminary mirror values that we are using because the Pop!_OS system doesn’t have Gentoo’s mirrorselect. We will overwrite them once we chroot in.

Entering the chroot

Now, we will begin the process of changing root (chrooting). Doing so will let us run programs and commands as if we are running our Gentoo system (whose root is at /mnt/gentoo) rather than the Pop!_OS system that we actually are.

To begin, we will copy over two files: one to configure the Gentoo package repository and the other to configure DNS settings. Make the necessary directories and copy the files with:

mkdir --parents /mnt/gentoo/etc/portage/repos.conf
cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf/gentoo.conf
cp --dereference /etc/resolv.conf /mnt/gentoo/etc/

Next, we are going to mount several special filesystems, which will allow virtual filesystems such as /dev and /sys to function correctly inside the chroot environment. Run the following commands to do so:

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
test -L /dev/shm && rm /dev/shm && mkdir /dev/shm 
mount --types tmpfs --options nosuid,nodev,noexec shm /dev/shm 
chmod 1777 /dev/shm

Finally, we are ready to actually enter the chroot. Run the following to change root to /mnt/gentoo, start bash on the Gentoo system, set our environment variables, and label the prompt to remind us that we are in a chroot:

chroot /mnt/gentoo /bin/bash 
source /etc/profile 
export PS1="(chroot) ${PS1}"

Syncing the repos and additional configuration

Congratuations, you are now logged into your Gentoo system for the first time! However, there is still a lot of work to do.

We will begin by getting our local copy of the Gentoo repository up to date. Run the following commands to get a mostly up-to-date copy of the repository, and then to update the few out-of-date packages to their newest version.

emerge-webrsync
emaint sync -a

Ignore the Invalid Repository Location error on first run on these commands. The necessary directory will be created and the error should not show up again. This process should take a few minutes to run, but it should be significantly faster on subsequent runs. Whenever you are updating the local package repository in the future, you will only have to run emaint sync -a.

Now that our local repository is up to date, we are notified that there are news items to read. Run eselect news list to see the names of all the news items and eselect news read n to read the n-th news item. As we are just now installing, none of these items should be pertinent but it is important to read them periodically as they may require action on your part.

Now that we are in the Gentoo system and have the mirrorselect package downloaded to our local repository, we can install mirrorselect to make sure that we are always downloading source code from the optimal mirror. Run the following to download, unarchive, configure, compile, and install mirrorselect and all of its dependencies:

emerge --ask --verbose app-portage/mirrorselect

In the above, emerge is the main command-line interface for Portage, --ask means that Portage will ask before installing, --verbose means that we will see extra output, and app-portage is the package category that mirrorselect is in. Press return to continue with the installation. Once mirrorselect has finished, we can run it and send the output to /etc/portage/make.conf. Run the following, scrolling through the list with the arrow keys, selecting mirrors with Space and finishing with Enter:

mirrorselect -i -o >> /etc/portage/make.conf

In order to delete our old specification of mirrors from /etc/portage/make.conf, we will need a text editor. If GNU nano is fine with you, use it right now to open the file. If not, install your favorite text editor as follows. Search for your favorite editor with emerge -s <my-favorite-editor>. Once you have determined the full name (typically app-editors/<my-favorite-editor> and referred to as an atom), install it with emerge -av <my-favorite-editor> (where -av is short for --ask --verbose). If you are not sure, you can also list all text editors with emerge -s "%@^app-editors". Depending on the size and complexity of the editor that you choose, the installation make take several minutes or longer.

Reopen /etc/portage/make.conf in your text editor, and delete the first line which says GENTOO_MIRRORS=..., saving the line which we just tacked onto the end of the file.

Next, we will set the system profile. Gentoo has a number of profiles corresponding to various computer architectures, init system, and use case combinations. Run eselect profile list to see all of the available profiles. Assuming that you are on an x86_64 (also known as AMD64) processor (everyone except for M1 Mac users), choose default/linux/amd64/<newest-stable-version> (stable) if you are running OpenRC and default/linux/amd64/<newest-stable-version>/systemd (stable) if you are running systemd. In the before, <newest-stable-version> is the newest version number which corresponds to a profile which is marked as (stable). To choose a profile, run eselect profile set n where n is the number that corresponds to the profile that you want. Run eselect profile show to confirm that you selected the right one.

Before we do any more emerging, it would be wise to make sure that Portage itself is up to date. Run the following to emerge Portage, updating it if new sources (source code) are available:

emerge -av --oneshot sys-apps/portage

In the above, --oneshot means that the sys-apps/portage package will not be added to the @world set, which is the set of all manually installed programs. This is desirable, as sys-apps/portage is already part of the @system set, which contains many default packages. It would thus be redundant to have sys-apps/portage in both sets.

Once Portage has finished updating, there are a few more settings that we want to set. First, look up your time zone string by following this link. For whichever time zone corresponds most to yours, write down the TZ database name. Then, run:

echo "<tz-database-name>" > /etc/timezone
emerge --config sys-libs/timezone-data

making sure to substitute in for your timezone rather than running the above as written.

We would also like to configure the system locale. If using American English, your locale string is en_US.UTF-8. If you want to use another language, please reference this table and make note of the locale column. once you have determined your locale(s), specify them in /etc/locale.gen as follows:

<locale-1> UTF-8
<locale-2> UTF-8
...

Now, run locale-gen to generate the locales specifices above. Run eselect locale list to list all of your system’s locales, and finally run eselect locale set n to select the n-th locale. Update the environment to set the new locale and fix the prompt with:

env-update && source /etc/profile && export PS1="(chroot) ${PS1}"

Make sure that your terminal prompt still says (chroot) before continuing. If it doesn’t, redo Entering the chroot and return here.

There is one more step to complete before we bootstrap the system. As we will be compiling the entire system from source, we may as well compile packages with CPU-specific optimizations. To tell Portage which optimizations to perform, we use a tool called cpuid2cpuflags. Install it with:

emerge -av app-portage/cpuid2cpuflags

Have it output the flags for your CPU and tack them onto the end of /etc/portage/make.conf with:

cpuid2cpuflags | sed "s/: /=\"/" | awk '{print $0"\""}' >> /etc/portage/make.conf

The before just modifies the output of cpuid2cpuflags to make it valid syntax for /etc/portage/make.conf.


5. Bootstrapping the system

In this step, we will recompile the entire Gentoo system, including the packages which came preinstalled on the stage 3 tarball. While the step is not completely necessary, and takes several hours, to get the full Gentoo experience, I would recommend that you do it. If not, skip to setting the desktop profile.

Before performing the actual bootstrap, let’s examine the script which we will be running. Enter the scripts directory with

cd /var/db/repos/gentoo/scripts

and open bootstrap.sh. There are several changes that we need to make to bootstrap.sh, having to do with the virtual C library, and improper USE flags for GCC3. In your text editor, search for the phrase should never fail. On the line just below it which has myLIBC, replace the && with a ;. Next, search for export USE="- and before the closing " add openmp so that the line reads export USE="-* bootstrap ${ALLOWED_USE} ${BOOTSTRAP_USE} openmp". Once you have made these changes, save and quit the file.

To preserve our /etc/locale.gen, which we already customized, run:

cp -v /etc/locale.gen{,.bak}

Now, run ./bootstrap.sh to start the bootstrap. As this will have to compile GCC, glibc, and a number of other massive packages, it will take several hours on average hardware.

Once this has finished, we need to make sure that a valid GCC profile was created. This is vital for our second run of the bootstrap script. Check this with:

gcc-config --list-profiles

If all went well, skip the following three commands. If not, i.e. the output tells you that the profile is invalid, run:

gcc-config 1
env-update && source /etc/profile && export PS1="(chroot) $PS1"
emerge --ask --verbose --oneshot sys-devel/libtool

Now that we’ve verified the validity of our GCC config, we will bootstrap the system again. This time, all of the tools which we used to build the toolchain will themselves be rebuilt with our personally-built compiler. Bootstrap the system again with ./bootstrap.sh and press Enter.

Once the second bootstrap completes, check the profiles again with gcc-config --list-profiles. If all is well, revert to the original /etc/locale.gen and regenerate the locales with:

mv -v /etc/locale.gen{.bak,}
locale-gen

Now that we’ve bootstrapped the toolchain (GCC, glibc, etc.), we will use it to recompile all of the other installed packages (the @world set and its dependencies). Change back to the root directory with cd / and create a file that will help us confirm if everything has been rebuilt with:

touch /tmp/prebuild_checkpoint

Finally, rebuild everything in @world with:

emerge -av --emptytree --with-bdeps=y @world

In the before, --emptytree tells Portage to recompile everything in @world, and --with-bdeps=y tells Portage to include build-time dependencies which are not required. This will take several hours, probably even longer than the first bootstrapping steps took.

Now, we will test to make sure the bootstrap was successful and all the old packages were removed. Make sure that old packages and dependencies were removed with emerge --depclean. Then, check for old files with:

find / -type d -path /boot/efi -prune -o -path /proc -prune -o -type f -executable -not -newer /tmp/prebuild_checkpoint -print0 2>/dev/null | xargs -0 file --no-pad --separator="@@@" | grep -iv '@@@.* text' 
find / -type d -path /boot/efi -prune -o -path /proc -prune -o -type f -not -executable -not -newer /tmp/prebuild_checkpoint -print0 2>/dev/null | xargs -0 file --no-pad --separator="@@@" | grep '@@@.*\( ELF\| ar archive\)'

These commands may take a while to run. If the above commands produce no output, then all executables and libraries were successfully rebuilt.


6. Switching to the desktop profile

Now that the base system has been completely built, we would like to build a number of utilities which are necessary for a basic desktop Linux experience. Luckily for us, these utilities are specified in the desktop profile, which we will now set. List all profiles with eselect profile list and find the one of the form default/linux/amd64/<latest-stable-version>/desktop (stable). Select that one with eselect profile set n and run eselect profile show to make sure you set the right one.

Setting USE flags

While mentioned briefly earlier, we will now get into USE flags. USE flags are parameters which allow you to specify which features are compiled into the various programs on your system. There are global use flags, which are in effect for every program, and program-specific use flags, which only apply for a single package or even a single package version. Global use flags often correspond to a particular piece of hardware or an important piece of software like an init system or display server, whereas package-specific use flags are more specialized, such as whether your bootloader is built with device mapper support.

For our system, we want to set our global USE flags in correspondence with our init system, display server, and audio server. For this tutorial, we will use XOrg as the display server, and PulseAudio as the audio server, and either systemd or OpenRC as the init system. Open /etc/portage/make.conf in your text editor, and at the bottom add a line which says USE="X elogind -systemd pulseaudio" if you are using OpenRC or USE="X systemd pulseaudio" is you are using systemd. This tells Portage to compile packages with or without support for each of these features specified. elogind will allow us to use software which typically depends on systemd without actually installing it.

In addition to global use flags, we must also specify information for our graphics cards and input devices. To determine your graphics card’s VIDEO_CARDS name, look it up on the Gentoo wiki and look for VIDEO_CARDS. After the line with use flags, add a line which says VIDEO_CARDS="<video-card-1> <video-card-2> ...". Note that multiple VIDEO_CARDS values may be required for certain graphics cards, and even more for systems with multiple GPUs. For my Intel-NVIDIA laptop, I have VIDEO_CARDS="intel i965 iris nvidia". If on a laptop with a trackpad, add another line with INPUT_DEVICES="libinput synaptics".

Once these settings are to your satisfaction, we will install the necessary packages. This command should bring in all of the necessary graphics and input device drivers, as well as many packages for desktop Linux support:

emerge -av --update --deep --newuse @world

where --update installs new versions of the packages if available, --deep consider non-immediate dependencies, and --newuse consider recent changes to USE flags (as we have just done).

Notice that running this command for the first time causes a circular dependency error. Portage tells you that it may be possible to fix it by changing a use flag. USE flags are specified in files within the /etc/portage/package.use directory. First, create a file in /etc/portage/package.use with touch /etc/portage/package.use/zzz_autounmask. When automatic USE flag changes are needed, they will be written to this file. Now, open /etc/portage/package.use/python in your text editor. In this file, we’ll specify the USE flag changes for Python. Simply add dev-lang/python -bluetooth (as suggested by Portage) to the file and save and quit. Re-run the above command (emerge -avuDN @world) and, if any USE flag errors are encountered, simply make a new file in /etc/portage/package.use with contents according to Portage’s suggestion. Once errors are resolved, you will begin emerging hundreds of packages, which will again take several hours.

As we made the previous USE change to temporarily avoid a conflict, we can revert it now that all the desktop packages are emerged. Delete this USE file with rm /etc/portage/package.use/python and re-emerge the relevant packages with:

emerge -avuDN @world

7. Configuring and compiling the kernel

Now that all of the system and desktop packages have been installed, it is time to configure and compile the Linux kernel, perhaps what Gentoo is most known for. This step requires by far the most time out of any, however that time comes from researching the proper kernel options for your hardware rather than from the actual compilation. If you don’t want to invest the time to configure your own kernel, continue reading but skip over the section on Configuring the kernel. However, I would again suggest that you do configure it yourself to get the whole Gentoo experience.

The Linux kernel is the most important piece of software that will be running on your machine. It is responsible for the allocation of resources between processes, communication between hardware and software, and a number of other critical tasks.

On Gentoo (and any other source-based distribution) the kernel is configured through .CONFIG files, specifying for each kernel option whether that option is enabled (OPTION=y), disabled (OPTION=n), or enabled as a module (OPTION=m). Upon downloading the kernel sources, a number of options are already set. These are common-sense options that you do not want to change unless you have a good, informed reason to do so.

Package keywords and licenses

Before installing the kernel sources and associated tools, we need to talk about package keywords and licenses in Gentoo. Specific versions of packages are marked with keywords to indicate their stability on each architecture. Stable packages on x86_64 systems are marked with amd64 while unstable ones are marked ~amd64. Unstable packages are still functional and mostly reliable, but it is my suggestion to only use unstable packages where there is a significant benefit in terms of security or features.

Following this reasoning, we will use the most recent (unstable of the Linux kernel). Which versions of packages to run (stable vs. unstable) are specified in the /etc/portage/package.accept_keywords file/directory. Open /etc/portage/package.accept_keywords in your text editor and add the line sys-kernel/gentoo-sources ~amd64. This means that we would like to use the most recent (non-git) kernel sources available.

Gentoo has a similar system for package licenses. By default, you can only install packages which are free and open source, and in source code form. To agree to a more restrictive license, you are required to make the /etc/portage/package.license file and list the packages and corresponding licenses one on each line. We will need to accept a no source code license for the Linux kernel firmware, as it includes closed-source firmware for some devices. To accept this license, open /etc/portage/package.license in your text editor and add the following line:

sys-kernel/linux-firmware linux-fw-redistributable no-source-code

Note that Portage will sometimes prompt you to make these changes automatically. If Portage asks you to make changes to your config files, type y and then hit Enter. Then run dispatch-conf, which allows you to visualize the changes that Portage would like to make. If you want to make the changes, press u, and if you want to discard them press z.

Before configuring the kernel, we must install several tools that will aide us in configuration and compilation. The first of these is genkernel, which helps us generate and initial RAM filesystem (initramfs). As our root filesystem is inside of a crypt volume on startup, we need somewhere to store the tools responsible for decrypting it. The initramfs plays this role. We also need cryptsetup to perform the actual encryption/decryption and lvm2 to manage our logical volumes. Finally, we will need the actual Linux kernel (in gentoo-sources) and associated firmware (linux-firmware).

Install all of the above with:

emerge -av gentoo-sources linux-firmware genkernel cryptsetup lvm2

Configuring the kernel

Finally, we are ready to configure the kernel. If you prefer to use a pre-configured kernel, skip to the section on compiling the kernel. If not, enter the kernel sources directory with cd /usr/src/linux. Open the configuration screen with make menuconfig. We will use this interface to change kernel options.

To navigate menus, use the arrow keys to go up and down, and use Enter to enter a submenu. Turn an option on/off/modular with y/n/m.

I don’t have space here for a detailed kernel configuration guide, but that will come in another post. For now, I’ll go over the kernel options which you will need to change to have a functioning system. There are two major categories: device drivers and options that are not hardware specific.

Configuring device drivers

Make a list of the major pieces of hardware in your machine (CPU, integrated graphics, graphics card, trackpad, touchscreen, USB ports, SD card slot, camera, microphone, etc.) and search for each one on the Gentoo wiki. Odds are that there will be an article for much of your hardware which details the specific kernel options which need to be set. Set the options as specified in each of the wiki articles, and use the left/right arrows to select Save and press Enter.

Other kernel options

There are a number of kernel options which it would be wise to set but do not correspond to a particular piece of hardware. I will detail those here in a concise format: the option name, followed by y for yes, n for no, and m for enabled as a module. You can search for any of these options in menuconfig by pressing /, entering the name, and pressing Enter. Note that the CONFIG_ at the beginning is not needed.

CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_EFI_PARTITION=y
CONFIG_BTRFS_FS=y
CONFIG_VFAT_FS=y

These above options allow you to view the .config file from the currently running kernel and enable support for the BTRFS and vFAT filesystems.

Once you have configured to your satisfaction, navigate with the left/right arrows to the Save option and press Enter. Then, press Escape until you exit menuconfig.

Compiling the kernel

Now that we have configured the kernel or chosen to let it be done automatically, it’s time to compile the kernel.

If the kernel was configured manually

If you configured the kernel yourself, the compilation is controlled by a Makefile in the Kernel source directory, so you can initiate the build with a simple make -j<threads-plus-one> where <threads-plus-one> is the number of threads on your system plus one. This command generates the necessary files (including the actual kernel executable) which will be copied onto the /boot partition.

Once this compilation is done, the modules can be copied to /lib with make modules_install and the kernel itself is installed with make install. To generate the initramfs which we discussed earlier, run:

genkernel --luks --lvm --kernel-config=.config initramfs

Letting genkernel decide kernel options

If you did not configure the kernel manually, kernel compilation and initramfs generation is done with one command:

genkernel --luks --lvm all

Note that using genkernel leads to far more kernel options and modules being enabled, leading to a much larger kernel executable and far longer compile times.


8. Miscellaneous configuration and installing tools

Now that the kernel configuration is out of the way, there are just a few more steps before we have a fully functioning minimal system.

fstab configuration

First, we need to write our filesystems table (fstab) which tells the system which partitions to mount at which mount points on startup. It also specifies the filesystem and various mounting options. Open /etc/fstab in your text editor and substitute in your boot partition for <boot-partition>:

# Drive/volume			Mount point	Filesystem	Mount options		Dump	  Fsck order
<boot-partition>                /boot           vfat		defaults,noatime        0	  2
/dev/mapper/gentoo-root         /               btrfs		noatime,discard         0	  1
/dev/mapper/gentoo-home         /home           btrfs		noatime,discard         0	  2
/dev/mapper/gentoo-swap         none            swap		defaults                0	  0

In the above fstab, the first column represent the drive to be mounted, the second where it is mounted, the third its expected filesystem, the fourth various mount options, the fifth whether or not it needs to be dumped, and the last the order in which filesystems should be checked. For more info about the fstab, see the Gentoo wiki. Save the file and quit.

Network configuration

Next, we will make a number of changes so that our system will be able to connect to the internet. First, open /etc/conf.d/hostname in your text editor and change the localhost in hostname="localhost" to what you would like your system to be named. Save the file and quit.

To allow easy connection to Wifi networks, we will install NetworkManager. We would like to ensure that whenever packages can be build with NetworkManager support, they are. Open /etc/portage/make.conf in your text editor and add networkmanager to the list of USE flags. Save the file and quit, and run the following to initiate the recompilation of packages:

emerge -avuDN @world

Before installing NetworkManager, we will set some USE flags which correspond to features that we have no change of using. Install gentoolkit with emerge -av app-portage/gentoolkit and run equery uses networkmanager. This prints out all of the possible USE flags, and whether or not they are currently enabled. For most people’s hardware wext and ppp will not be useful, so they are worth disabling for the compilation time and space saved. Open /etc/portage/package.use/networkmanager in your text editor and add the text net-misc/networkmanager -ppp -wext. Save the file and quit. Now that these USE flags are set, emerge NetworkManager with emerge -av networkmanager.

Once NetworkManager finishes, we just need to edit the hosts file. Open /etc/hosts in your text editor and paste in the following contents, where <hostname> is the hostname you set earlier:

127.0.0.1       <hostname>.localdomain	localhost
::1             localhost		<hostname>

Users and accounts

Up until now, we only have one user account on the system, the root account. It is best practice to use a non-privileged account for any task that doesn’t need root privileges. Set the root password with passwd and create a new account with your name with:

useradd -m -G users,wheel,audio,video <your-name>

Set the password of your new account with passwd <your-name>. Note that characters will not appear as you type them into passwd.

Additional tools and services

Before we install the bootloader, there are a few system tools which we should install. These include a logger to view system logs (syslog-ng), a cron program to perform tasks every fixed amount of time (cronie), a tool to index the filesystem for quick searches (mlocate), tools for filesystem manipulations (btrfs-progs and dosfstools), and a minimal alternative to sudo (doas). Install all of these with:

emerge -av syslog-ng cronie mlocate btrfs-progs dosfstools app-admin/doas

Add some of these tools as well as the LVM service to start on boot with:

rc-update add syslog-ng default
rc-update add cronie default
rc-update add lvm boot

Finally, we want to configure doas to let us execute commands as root without reentering the password for every command. Open /etc/doas.conf in your text editor and add the contents permit persist :wheel4.


9. Installing and configuring bootloader

Only one major step remains before we have a functioning minimal system, and that is setting our machine up to boot the system which we have built. To do this, we will install a bootloader called GRUB, and use it to boot our system.

Before we install GRUB, we need to set several USE flags to make sure it works with our dm-crypt LVM setup. These use flags can be set in one line with:

echo "sys-boot/grub mount device-mapper" > /etc/portage/package.use/grub

and the package installed with emerge -av grub.

Once GRUB finishes installing, we need to tell it which volume is going to be decrypted, that it is an LVM volume, as well as which filesystem will be mounted. This information is communicated through the GRUB_CMDLINE_LINUX variable in /etc/default/grub. Open /etc/default/grub in your text editor and add the following line:

GRUB_CMDLINE_LINUX="crypt_root=<lvm-partition> root=/dev/mapper/gentoo-root rootfstype=btrfs dolvm dobtrfs quiet"

where <lvm-partition> is the partition which contains the crypt LVM from before. Save the file and quit.

Now, we are ready to install the bootloader into our EFI partition. Do so with the following command, substituting in your boot drive, not partition (e.g /dev/sda rather than /dev/sda1 or /dev/nvme0n1 rather than /dev/nvme0n1p1):

grub-install --target=x86_64-efi --efi-directory=/boot <boot-drive>

Finally, instruct GRUB to recognize your kernel files with the following:

grub-mkconfig -o /boot/grub/grub.cfg

Rebooting into the system

To exit the Pop!_OS system and reboot into Gentoo, run the following commands:

exit
cd
umount -l /mnt/gentoo/dev{/shm,/pts,}
swapoff /dev/mapper/gentoo-swap
umount -R /mnt/gentoo
reboot

Make sure to select Gentoo Linux or GRUB in your boot menu upon reboot. If all goes well, you will be greeted by a prompt to enter the password for the crypt volume. Enter this password, and after a little while you will see system services starting and be prompted to log into your account. Enter your newly created username and password and you should be dropped into a bash shell. Congratulations, you have built yourself a minimal Gentoo system!

References


  1. The Arch Wiki on burning an ISO to a USB drive with dd ↩︎

  2. Sakaki’s EFI install guide on verifying the stage 3 tarball ↩︎

  3. Sakaki’s EFI install guide on bootstrapping ↩︎

  4. The Gentoo wiki on doas configuration ↩︎