# Preparation
## Minimal Installation CD
### What is it?
The *Minimal Installation CD* is the default method of installing Gentoo. The Minimal Installation CD is an ISO file that contains a minimal Gentoo environment which we can work within to install Gentoo.
### How do you set it up?
#### Step 1 - Downloading it
You can download it by going to [gentoo.org/downloads](https://www.gentoo.org/downloads).
Download the Minimal Installation CD which is compatible with the architecture of the system you are installing it to. This guide assumes you will be installing Gentoo on a machine that is 64-bit, which most computers are. If you aren't sure what architecture your target computer is, follow these steps for the operating system which is currently installed on it:
* For Windows, press the Windows key and R, type *msinfo32*, press enter, and find where it says *System Type*. If it says *x64-based PC*, that means the system is 64-bit.
* For MacOS, click on the Apple logo at the top left, then *About This Mac*, and find where it shows the processor. If the processor isn't an Intel Core Solo or Intel Core Duo, the system is 64-bit. Otherwise, it's 32-bit.
* For Linux, run `uname -a`. If the output of the command contains *x86_64*, that means the system is 64-bit.
For 64-bit machines, you'll want to download the Minimal Installation CD for *amd64.*
> [!note]
> If you've ever wondered why operating systems are distributed as ISO files (which are meant for optical media like discs), I found [this post from a Rufus developer](https://superuser.com/questions/1170832/why-are-there-different-options-for-creating-bootable-usb-compared-to-a-cd) which was very insightful.
#### Step 2 - Burning it
Once the Minimal Installation CD is downloaded (which shouldn't take too long) it will need to be burned onto a removable medium like a CD or a USB drive. By burning it to a removable medium, we can boot into the Minimal Installation CD's environment and install Gentoo on our target system. The program you will use to do this depends on what operating system you used to download the ISO file and what type of removable media you are going to burn the ISO file to.
##### Method 1 - Using a USB device
For Windows, MacOS, and Linux, you can download and run a program called [balenaEtcher](https://etcher.balena.io/). The process for flashing an ISO file with this program is very simple.
If you're using Linux and don't want to install an extra program you'll only use once, you also have the option of using `dd` by running:
```bash
$ dd if=install-amd64-minimal-<release timestamp>.iso of=/dev/<drive name> bs=4096 status=progress && sync
```
If the `dd` command looks like it isn't doing anything, just leave it alone for a few more minutes. I promise that it is.
##### Method 2 - Using a CD
For Windows 7 and older, you can burn an ISO file to a CD by right clicking on the drive in Explorer and clicking *Burn disk image.*
For MacOS, you can use the Disc Utility that's built into the operating system.
For Linux, you can use `cdrecord` to burn an ISO file to a CD like so:
```bash
$ cdrecord dev=/dev/sr0 install-amd64-minimal-<release timestamp>.iso
```
> [!note]
> There are many programs which can burn ISO files to removable drives. I chose balenaEtcher because it has the simplest user interface. Rufus is another alternative if you're on Windows and you're burning the ISO file to a USB drive. If you're already using Linux, then you should be familiar with executing commands like `dd` in the terminal and so it would be redundant to install another program.
If the process was successful, you should now have a USB drive or CD that has the official Gentoo installation media ready to use.
> [!warning]
> Operating systems treat USB drives and CDs differently. Make sure you're not trying to use a program meant for USB devices with a CD, and vice versa.
# Storage
```bash
# Create the key file using random bytes.
$ sudo dd if=/dev/urandom of=/opt/home.key bs=1 count=64
# Make the key file inaccessible to other users besides root.
$ sudo chmod 600 /opt/home.key
# Add the key file to the LUKS header of the home partition
$ sudo cryptsetup luksAddKey /dev/nvmen0p2 /opt/home.key
```
```bash
# /etc/conf.d/dmcrypt
target=home
key="/opt/home.key"
source=UUID=<UUID of LUKS-formatted home partition>
```
# Memory
## Zswap
### What is it? [(1)](https://www.kernel.org/doc/html/v4.18/vm/zswap.html)
Zswap is a feature of the Linux kernel which provides a compressed cache for swap memory pages in RAM. Anything that would normally be stored on a swap partition or file on a disk would instead be compressed and stored into a memory pool.
Once the memory pool is full, the least recently used (LRU) swap page is evicted and stored to the backing swap device, like a swap partition or a swap file.
### Why would you want to use this?
If reads from a compressed cache are faster than reads from the swap device, there's the potential for significant performance improvements.
### How do you set it up?
There are many ways to enable this. The way that I chose to do it was by writing a script and saving it at `/etc/local.d/zswap.start`:
```bash
#!/bin/bash
echo lz4 > /sys/module/zswap/parameters/compressor
echo 1 > /sys/module/zswap/parameters/enabled
```
The `local` service is meant for these kinds of lightweight scripts which are non-blocking and terminate very quickly.
> [!warning] Warning
> After creating a script in `/etc/local.d/`, make it executable with `chmod +x`.
## Encrypted Swapfile
To create an encrypted swapfile, run the following commands:
```bash
$ fallocate -l <size>GiB /opt/swapfile
$ chmod 600 /opt/swapfile
$ cryptsetup --type plain -d /dev/urandom open /opt/swapfile swapfile
$ mkswap /dev/mapper/swapfile
$ swapon /dev/mapper/swapfile
```
I was running into a problem when I initially set up my swapfile. Normally, for any LUKS-encrypted block device or file, you add a corresponding entry for it in the `/etc/conf.d/dmcrypt` file and the `/etc/fstab` (filesystem table) file to ensure that it's properly mounted at boot.
> [!example] Examples of the entries I wrote for my swapfile
> ```bash
> # /etc/conf.d/dmcrypt
>
> swap=swapfile
> source=/opt/swapfile
> options="--type plain --key-file /dev/urandom"
> ```
>
> ```bash
> # /etc/fstab
>
> # fs mountpoint type opts dump pass
> /dev/mapper/swapfile none swap sw 0 0
> ```
However, this configuration didn't work. How come? The reason why is because the root filesystem is mounted as read-only when `dmcrypt` processes the entries in `/etc/conf.d/dmcrypt`. When `dmcrypt` reaches the entry for the swapfile, it runs `mkswap` on it. Since `mkswap` requires the ability to write to the disk, it fails.
Here is a snippet from the `/var/log/rc.log` file which shows `dm-crypt` failing to set up the swapfile due to `mkswap` not being able to write to the disk:
```bash
* swap using: --type plain --key-file /dev/urandom create swap /opt/swapfile ...
WARNING: Using default options for cipher (aes-xts-plain64, key size 256 bits) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
[ ok ]
* pre_mount: mkswap /dev/mapper/swap ...
mkswap: unable to erase bootbits sectors
[ !! ]
* Failed to setup dm-crypt devices
[ !! ]
* ERROR: dmcrypt failed to start
...
* Activating swap devices ...
swapon: /dev/mapper/swap: read swap header failed
```
To get around this, I wrote a small script in the `/etc/local.d/` directory which sets up a swapfile.
```bash
# /etc/local.d/swapfile.start
#!/bin/bash
SWAP_FILE_NAME=swapfile
SWAP_FILE_PATH="/opt/${SWAP_FILE_NAME}"
SWAP_FILE_MAPPER_PATH="/dev/mapper/${SWAP_FILE_NAME}"
if [ ! -e "/dev/mapper/${SWAP_FILE_NAME}" ]; then
cryptsetup close swapfile
chmod 600 "$SWAP_FILE_PATH"
cryptsetup --type plain -d /dev/urandom open "$SWAP_FILE_PATH" "$SWAP_FILE_NAME"
mkswap "$SWAP_FILE_MAPPER_PATH"
swapon "$SWAP_FILE_MAPPER_PATH"
fi
```
# Booting
```bash
# /etc/portage/package.use/boot
sys-kernel/installkernel -dracut -generic-uki -grub -systemd-boot ugrd -uki -ukify refind
```
# Audio
Besides networking, the second most challenging thing I've found difficult when installing a Linux distribution like Arch Linux or Gentoo is getting audio to work correctly.
```bash
# /etc/portage/package.use/pipewire
media-plugins/gst-plugins-pulse abi_x86_32
media-sound/pulseaudio udev
media-video/pipewire echo-cancel extra ffmpeg pipewire-alsa sound-server v4l
media-video/wireplumber elogind
```
```bash
$ sudo emerge -av media-video/pipewire media-video/wireplumber
```
```bash
# Check if the audio kernel modules are loaded.
$ lsmod | grep snd
# Find which kernel modules are being used by your sound cards.
$ lspci -k | grep -A4 -i audio
# Find which audio codecs are being used by your sound cards.
$ cat /proc/asound/card*/codec* | grep Codec
```
> [!example] Example of the kernel modules and audio codes being used by my sound cards and what I put in the `/etc/modprobe.d/` directory
> ```bash
> $ lspci -k | grep -A4 -i audio
> 08:00.1 Audio device: NVIDIA Corporation GP104 High Definition Audio Controller (rev a1)
> Subsystem: ZOTAC International (MCO) Ltd. Device 2435
> Kernel driver in use: snd_hda_intel
> Kernel modules: snd_hda_intel
> 09:00.0 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050] (rev a1)
> --
> 09:00.1 Audio device: NVIDIA Corporation GP107GL High Definition Audio Controller (rev a1)
> Subsystem: Dell Device 11c0
> Kernel driver in use: snd_hda_intel
> Kernel modules: snd_hda_intel
> 0a:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Zeppelin/Raven/Raven2 PCIe Dummy Function
> --
> 0b:00.3 Audio device: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) HD Audio Controller
> Subsystem: ASUSTeK Computer Inc. Device 8723
> Kernel driver in use: snd_hda_intel
> Kernel modules: snd_hda_intel
> ```
>
> ```bash
> $ cat /proc/asound/card*/codec* | grep Codec
> Codec: Nvidia GPU 83 HDMI/DP
> Codec: Nvidia GPU 80 HDMI/DP
> Codec: Realtek ALC1220
> ```
>
> ```bash
> # /etc/modprobe.d/snd.conf
> options snd_hda_intel model=alc1220
> ```
# Users
## User Services
### How do you set it up?
#### Step 1 - Check the version of OpenRC
You need to make sure you have OpenRC 0.60 or newer. You can check what version of OpenRC is installed on your system by running the following command:
```bash
$ openrc --version
```
If it shows that you have the required version of OpenRC, you can skip to step 4. Otherwise, continue to the next step.
#### Step 2 - Unmask the testing branch for OpenRC
OpenRC version 0.62.2 is the latest version on the testing branch (at the time of writing this), which is what you'd need to install.
However, Portage doesn't install packages on the testing branch by default unless you've allowed it to in `/etc/portage/make.conf` (allowing it to install all testing branch packages) or in `/etc/portage/package.accept_keywords` (allowing it to install specific testing branch packages). If you try to emerge OpenRC without the necessary configuration in Portage, it will ignore version 0.62.2 and install the latest version on the stable branch, which is 0.56 (at the time of writing this).
To allow Portage to install the newer version, add an entry like the following to your `/etc/portage/package.accept_keywords` directory (or file):
```bash
# /etc/portage/package.accept_keywords/openrc
sys-apps/openrc ~amd64
```
> [!warning]
> You can also add this line to your `/etc/portage/make.conf`:
> ```bash
> # /etc/portage/make.conf
>
> ACCEPT_KEYWORDS="~amd64"
> ```
>
> This would allow Portage to install all packages on the testing branch. However, the handbook strongly advises against this for most users as it may lead to instability or other errors. As well, it's difficult to switch back to using all stable branch packages once you've installed all testing (or unstable) branch packages.
#### Step 3 - Update OpenRC
Update OpenRC without adding it to the `@selected-packages` set with the following command:
```bash
$ sudo emerge -av --oneshot sys-apps/openrc
```
> [!warning] Warning
> It's important that OpenRC isn't added to the `@selected-packages` set because it's pulled in as a dependency of `virtual/service-manager`. The selected-packages set is meant to contain packages that were explicitly installed by the system administrator (like whenever you call `emerge -av <package name>`). It needs to contain as few dependencies as possible to avoid problems in dependency resolution when updating the system.
To verify that the correct version of OpenRC was installed, run the following command:
```bash
$ openrc --version
```
#### Step 4 - Enable per-user services for a specific user
To allow per-user services for a specific user, a symbolic link needs to be created from `/etc/init.d/user.<user>` (where `<user>` is the name of the user) to `/etc/init.d/user`. `user.<user>` is treated like a service by OpenRC, which when started creates a session for any services started under that user. This session starts when the system boots and stops when the system shuts down. In that time, the user can log in and log out without affecting the service.
```bash
$ sudo ln -s /etc/init.d/user /etc/init.d/user.<user>
$ sudo rc-update add user.<user> default
$ sudo rc-service user.nate start
```
#### Step 5 - Ensure the required directories and init scripts are created
There are two directories which OpenRC searches for user init scripts in:
1. `/etc/user/init.d` - Init scripts which are accessible to every user on the system. This directory is created automatically.
2. `/home/<user>/.config/rc/init.d` - Init scripts which are accessible only to the specific user whose home directory they're stored in. This directory isn't automatically created.
User init scripts are sometimes provided by packages, but more-than-likely you'll need to write your own. You can modify an existing init script meant for the entire system and adapt it to work for a user.
> [!example] Example of a user init script for Syncthing which was adapted from the system-wide init script
> ```bash
> #!/sbin/openrc-run
>
> SYNCTHING_HOMEDIR="${SYNCTHING_HOMEDIR:-${XDG_STATE_HOME:-$HOME/.local/state}/syncthing}"
> SYNCTHING_UMASK=${SYNCTHING_UMASK:-007}
> SYNCTHING_IONICE=${SYNCTHING_IONICE:-0}
> SYNCTHING_NICE=${SYNCTHING_NICE:-0}
> SYNCTHING_GUI_ADDRESS=${SYNCTHING_GUI_ADDRESS:-http://127.0.0.1:8384}
>
> description="Syncthing is an open, trustworthy and decentralized cloud storage system"
> command="/usr/bin/syncthing"
> command_args="--home=${SYNCTHING_HOMEDIR} --no-browser \
> --gui-address=${SYNCTHING_GUI_ADDRESS} ${SYNCTHING_OPTS}"
> pidfile="${XDG_RUNTIME_DIR:-/run/user/1000}/${RC_SVCNAME}.pid"
> command_background="yes"
> umask="${SYNCTHING_UMASK}"
> start_stop_daemon_args="--ionice ${SYNCTHING_IONICE} \
> --nicelevel ${SYNCTHING_NICE}"
>
> start_pre() {
> checkpath -d $SYNCTHING_HOMEDIR
> }
> ```
> [!warning] Warning
> After creating a user init script, make it executable with `chmod +x`.
#### Step 6 - Add and/or start user services
To add a user service to the default runlevel of the current user, you can run the following command:
```bash
$ rc-update --user add <service name> [default]
```
To start a user service, run the following command:
```bash
$ rc-service --user <service name> start
```
> [!warning]
> You need to run any `rc-* --user` commands as the user they're meant to affect. These shouldn't be run as the root user or be run with elevated privileges.
## XDG Base Directory Specification
### What is it?
The Cross-Desktop Group (XDG) was the previous name of freedesktop.org. One of the things they create are specifications for interoperability between desktop systems.
A base directory is a starting or root folder for the rest of an application or system.
The XDG Base Directory Specification is a specification that defines the locations for base directories which store user-specific files.
### Why would you want to use this?
Personally, I prefer having user-specific data organized into discrete areas depending on what type they are. I just like the look of a clean home directory. If you don't mind where programs store their data in your home directory, then this set up isn't required.
### How does it work?
This specification is implemented by defining environment variables. These environment variables store the paths of the base directories for each type of user-specific data. Programs which support to the XDG Base Directory Specification will read these environment variables and store user-specific data in their appropriate directories.
* `XDG_CACHE_HOME`.
* `XDG_CONFIG_HOME`.
* `XDG_DATA_HOME`.
* `XDG_STATE_HOME`.
* `XDG_DATA_DIRS`.
* `XDG_CONFIG_DIRS`.
### How do you set it up?
Since this is a specification for user-specific data, it's best to define these variables in a file that is only sourced at the user-level. One of the ways you can do this is by adding them to the shell run configuration (rc) file, like `~/.bashrc` or `~/.zshrc`.
If a program doesn't support the XDG Base Directory specification automatically, there are ways to get them to. Usually, this involves creating aliases in that same run configuration file which include flags or parameters that explicitly tell the program to use the XDG Base Directory.
> [!example] Example from my `~/.bashrc`
> ```bash
> # === PATH directories. === #
> PATH="${PATH}:/home/nate/.local/bin:"
> PATH="${PATH}:/home/nate/.local/src/fantom-1.0.81/bin:"
> LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH"
>
> # === XDG Base Directory Specification. === #
>
> # The directories.
> export XDG_CACHE_HOME="$HOME/.cache"
> export XDG_CONFIG_HOME="$HOME/.config"
> export XDG_DATA_HOME="$HOME/.local/share"
> export XDG_STATE_HOME="$HOME/.local/state"
>
> # Ensuring applications use the directories.
> export GNUPGHOME="$XDG_DATA_HOME"/gnupg
> export GOMODCACHE="$XDG_CACHE_HOME"/go/mod
> export GOPATH="$XDG_DATA_HOME"/go
> export GTK2_RC_FILES="$XDG_CONFIG_HOME/gtk-2.0/gtkrc":"$XDG_CONFIG_HOME/gtk-2.0/gtkrc.mine"
> export HISTFILE="$XDG_STATE_HOME"/bash/history
> export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME"/java
export MLUA_PATH="$HOME/.local/src/MicroLua"
> export OLLAMA_MODELS=$XDG_DATA_HOME/ollama/models
> export PYENV_ROOT=$XDG_DATA_HOME/pyenv
> export PYTHON_HISTORY=$XDG_STATE_HOME/python_history
> export PYTHONPYCACHEPREFIX=$XDG_CACHE_HOME/python
> export PYTHONUSERBASE=$XDG_DATA_HOME/python
> export W3M_DIR="$XDG_STATE_HOME/w3m"
> export WGETRC="$XDG_CONFIG_HOME/wgetrc"
> export VIMRC="$XDG_CONFIG_HOME/vim/vimrc"
> export XAUTHORITY="$XDG_RUNTIME_DIR"/Xauthority
>
> # === Aliases. === #
> alias wget='wget --hsts-file="$XDG_CACHE_HOME/wget-hsts"'
> ```
# Tuning
## CPU Governors
```bash
# /etc/local.d/cpu-governor.start
#!/bin/bash
for c in $(ls -d /sys/devices/system/cpu/cpu[0-9]*); do
echo performance >$c/cpufreq/scaling_governor;
done
```
## NVIDIA
```bash
$ nvidia-settings
```
# Programs and Packages
> [!note]
> * [[Gentoo For Embedded Development Guide]].
> * [[Gentoo For Virtual Machines]].
# Games
## Garry's Mod
The native Linux version of Garry's Mod was frequently crashing on my system.
Running the game with Proton Experimental seemed to fix most of the issues I was having.