Wizardless Linux
(upd. )

An installation wizard makes two assumptions:

  • It knows all the initial configurations a user may want
  • It won’t explode in the user’s face

Arch, Gentoo, and Void Linux have very hand-tunable initial installations, so not only does a wizard become harder to make, but it’s often more worthwhile to have users understand their system and how to read its manuals. Plus, it’s fun to DIY!

That being said, if you have too much fun, then your system will be a shop car that never gets out of the garage.

Let’s install a wizardless Linux.

1. prepare a usb

  • Get and verify a disk imagea bit-by-bit copy of a drive (Arch §1.1 - §1.2, Gentoo, Void)
    • If you plan to unload a rootfs tarballa TAR archive of everything under / onto your internal drive, you can use any distro’s disk image
    • Verify the image isn’t corrupted by checking if the image’s sha256sum hash is in your respective sha256sum.txt
      # Linux example
      $ grep "$(sha256sum linux-mint.iso)" sha256sum.txt
      # Windows example
      $ CertUtil -hashfile cool-linux-distro.iso SHA256
      $ find "PASTE_HASH_HERE" sha256sum.txt
      
    • Verify the image is authentic using a key verification (Arch §1.2, Gentoo, Void) (might need GPG4Win or minisign)
  • Write the disk image onto a thumb drive, then boot
    • Click-and-drag the .iso image onto a Ventoy-formatted USB, or use Rufus, or see Gentoo § Writing
    • Boot from the thumb drive by either changing your computer’s boot order in the BIOS, or by entering your boot manager’s boot menu (C EXIT <ENTER> in a GRUB menu)

1.5 text

  • If you aren’t QWERTY/US or your console font has a poor size, see Arch §1.5

  • If needed, double a console font size via setfont -d

  • Instead of using alt+arrow or ctrl+alt+function key, consider splitting the console with

    tmux or screen

    C refers to CTRL.

    actiontmuxscreen
    split up/downC-b, “C-a, S
    split left/rightC-b, %C-a, |
    change windows*C-b, arrow keysC-a, tab
    *initialize new areaC-a, s
    logout of areaC+dC+a, Q

2. wifi

Unless you’re using ethernet, mobile tethering, or an offline install setup, connect to WiFi with

For example,

# a. iwctl
$ iwctl station wlan0 connect mySSID

# b. wpa_supplicant (interface assumed to be wlp2s0)
$ wpa_supplicant -B -i wlp2s0 -c \
  <(wpa_passphrase mySSID myPassword)

# c. nmcli (if on some other distro)
$ nmcli dev wifi con mySSID --ask

Note that your system’s time cannot be wonky if you want SSL and HTTPS to work.

$ hwclock --systohc # or
$ chronyd -q # for Gentoo

(Sources for timesync commands: Arch §3.3, Gentoo)

3. diskpart

Gentoo § Disks introduces x86_64-efi disks immaculately, and ArchWiki: Partitioning also discusses disks for other systems (e.g. non-UEFI BIOS boot systems).

As a format-disk tool, fdisk (and its simpler curses TUI cfdisk) are safer to use for some compared to parted, as fdisk confirms the writing at the end of the total operations rather than at the end of each individual operation.

In general,

As an example, we’ll set up:

  • a fat32 EFI partition on /dev/sda1
  • Btrfs subvolumes that separate /, /var/log, and /home (and has /.snapshots)
    • /var/log: logfiles shouldn’t be rolled back if we roll back /
    • /home: rolling back user files doesn’t necessitate rolling back root
    • mount options:
      • noatime preserves SSDs by not wasting a write on file access times
      • compress=zstd means compress=zstd:3 (compress at Zstandard level 3)
        • Higher levels denote more CPU cycles spent in hope of deeper compression, though 3 is a well-respected default. One may increase level or consider a more fervent compress-force=zstd.
# Partition disk (assuming EFI system)
#   /dev/sda1  512MiB    EFI System
#   /dev/sda2  the rest  Linux filesystem
cfdisk /dev/sda

# Format boot sector: ESP (EFI System Partition)
#   On a BIOS system, the boot sector must
#   *instead* be /dev/sda1 as a 1MiB BIOS boot
#   partition, without a formatted filesystem
mkfs.fat -F32 /dev/sda1

# I will name my LUKS partition "cryptroot"
#   Name it as you please
#   Note: `--pbkdf pbkdf2` is for
#         LUKS2 + GRUB2 compatibility.
#         See savannah.gnu.org/bugs/?59409
#         and ArchWiki: Dm-crypt/Encrypting_an_entire_system
#                       bottom of § 1
cryptsetup luksFormat --pbkdf pbkdf2 /dev/sda2
cryptsetup luksOpen /dev/sda2 cryptroot

# BTRFS
#   Didn't LUKS? Use /dev/sda2 instead
#   Label this ROOT so you can differentiate
#   entries in lsblk -f (lists blocks with UUIDs)
mkfs.btrfs -L ROOT /dev/mapper/cryptroot
mount -t btrfs -o \
  noatime,compress=zstd \
  /dev/mapper/cryptroot --mkdir /mnt

# BTRFS subvolumes
#   @ is just a subvol naming convention.
#   Later, we'll unmount our BTRFS "holder"
#   thing, then mount the subvolumes
btrfs subvolume create /mnt/@
btrfs sub c /mnt/@home
btrfs sub c /mnt/@var_log
btrfs sub c /mnt/@snapshots
umount /mnt

# BTRFS subvolume mounting
mount -t btrfs -o \
  noatime,compress=zstd,subvol=@ \
  /dev/mapper/cryptroot \
  --mkdir /mnt
mount -t btrfs -o \
  noatime,compress=zstd,subvol=@home \
  /dev/mapper/cryptroot \
  --mkdir /mnt/home
mount -t btrfs -o \
  noatime,compress=zstd,subvol=@var_log \
  /dev/mapper/cryptroot \
  --mkdir /mnt/var/log
mount -t btrfs -o \
  noatime,compress=zstd,subvol=@snapshots \
  /dev/mapper/cryptroot \
  --mkdir /mnt/.snapshots

# Boot sector
mount /dev/sda1 --mkdir /mnt/efi

4. shove the base system in

Arch users using pacstrap should select mirrors now, since it will be copied from the base system to the new system (source):

# Arch example (needs reflector package)
reflector --country US --protocol https \
  > /etc/pacman.d/mirrorlist

To shove in the base system, either use your package manager (pacstrap or xbps):

# Arch example
#   Install btrfs-progs if you used btrfs
#   Consider installing a text editor (nano/vim/neovim)
#   Consider installing man(uals)
#   You may need to run `pacman -Sy archlinux-keyring`
pacstrap -K /mnt base linux linux-firmware man neovim btrfs-progs

# Void example
mkdir -p /mnt/var/db/xbps/keys
cp /var/db/xbps/keys/* /mnt/var/db/xbps/keys
XBPS_ARCH=x86_64-musl
REPO=https://repo-default.voidlinux.org/current/musl
xbps-install -S -r /mnt -R "$REPO" base-system

or verify and use a rootfs tarball (Arch, Gentoo, Void):

# Arch example (get tar.zst, tar.zst.sig, sha256sums.txt)
# NOTE: This is a lazy method unendorsed by the ArchWiki
SOME_MIRROR=https://mirror.rackspace.com/archlinux
LATEST=archlinux-bootstrap-x86_64.tar.zst
wget $SOME_MIRROR/iso/latest/$LATEST{,.sig}
wget https://archlinux.org/iso/latest/sha256sums.txt
sha256sum -c sha256sums.txt | grep --color $LATEST
gpg --keyserver-options auto-key-retrieve --verify \
  $LATEST.sig $LATEST
tar xpvf $LATEST --xattrs-include='*.*' --numeric-owner -C /tmp
# Copy everything lazily
cp --archive /tmp/root.x86_64/* /mnt
# And uncomment a nice mirror
vim /mnt/etc/pacman.d/mirrorlist

# Gentoo example (get .tar.xz, .tar.xz.asc, .tar.xz.sha256,)
SITE=https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-hardened-openrc
LATEST=$(curl -L $SITE \
  | grep -Eo 'stage3-amd64-hardened-openrc-[0-9]{8}T[0-9]{6}Z.tar.xz' \
  | sort | tail -1)
wget $SITE/$LATEST{.sha256,.asc,}
sha256sum -c $LATEST.sha256
wget -O - https://qa-reports.gentoo.org/output/service-keys.gpg \
  | gpg --import
gpg --verify $LATEST{.asc,}
tar xpvf $LATEST --xattrs-include='*.*' --numeric-owner -C /mnt

# Void example
SITE=https://repo-default.voidlinux.org/live/current/
LATEST=$(curl $SITE \
  | grep -Eo 'void-x86_64-musl-ROOTFS-[0-9]{8}.tar.xz' \
  | sort | tail -1)
wget $SITE/{$LATEST,sha256sum.sig,sha256sum.txt}
sha256sum -c sha256sum.txt | grep --color $LATEST
LATEST_DATE=$(echo $LATEST | grep -Eo '[0-9]{8}')
wget https://raw.githubusercontent.com/void-linux/void-packages/master/srcpkgs/void-release-keys/files/void-release-$LATEST_DATE.pub
minisign -V -p void-release-20240314.pub \
  -x sha256sum.sig -m sha256sum.txt
tar xpvf $LATEST --xattrs-include='*.*' --numeric-owner -C /mnt
# Yes, the Void rootfs guide only says tar xvf. They're wrong.
Why --xattrs-include='*.*'?

Preserving all extended attrs (such as the setUID bit) are important for security software such as Pluggable Authentication Module. It is the difference between being able to login during a lockscreen:

$ ls -l /sbin/unix_chkpwd
-rwsr-sr-x 1 user arch 26640 Apr 11 04:47 /sbin/unix_chkpwd

and being locked out (unless you switch to a console):

$ ls -l /sbin/unix_chkpwd
-rwxr-xr-x 1 user arch 26640 Apr 11 04:47 /sbin/unix_chkpwd

This idea is taken from the Gentoo § The stage file § Installing a stage file and should be used when directly unpacking a rootfs onto a mountpoint

5. configure before boot

Now, based on Arch §3, we need to look at:

  • Arch §3.1 file system table/etc/fstab says what filesystem should mount where

  • Arch §3.2 change rootCommands will treat /mnt, or whatever directory, as /

    • Use a wrapper (arch-chroot from arch-install-scripts, or Void’s xchroot)
      $ arch-chroot /mnt
      
    • or manually chroot. See Gentoo § Installing a base system § Chrooting
    • /etc/resolv.conf may need to be copied to the new environment beforehand
      $ cp --dereference /etc/resolv.conf /mnt/etc/
      
  • If you untar’d a rootfs earlier, update your system.

    # Arch
    pacman-key --init
    pacman-key --populate
    pacman -Syu base linux linux-firmware man neovim
    # Void
    xbps-install -Su xbps
    xbps-install -u
    xbps-install base-system
    xbps-remove base-container-full
    # Gentoo
    # emerge-webrsync may be needed depending on firewall
    emerge --sync
    

    (command sources: Arch, Gentoo, Void)

  • Arch §2.1/Gentoo/Void (optional) Configure mirrors

    • For Gentoo’s mirrorselect, space enables/disables a selection, enter hits OK
    • See above (start of section 4. shove system) for Arch reflector example
  • Arch §3.3 - §3.4 localize timezone, locale

    # TIMEZONE =============
    # First:
    #   Find closest REGION/CITY by tab completion
    ls -l /usr/share/zoneinfo/REGION/CITY
    # Second:
    #   Declare timezone to your init system
    #   A: systemd (Arch) or runit (Void)
    ln -sf /usr/share/zoneinfo/REGION/CITY /etc/localtime
    hwclock --systohc
    #   B: OpenRC (Gentoo)
    echo "REGION/CITY" > /etc/timezone
    emerge --config sys-libs/timezone-data
    # Third:
    #   Init your time sync daemon
    #   A: systemd
    systemctl enable systemd-timesyncd
    #   B: OpenRC
    emerge --ask chrony
    rc-update add chronyd default
    #   C: runit
    xbps-install -y chrony
    sudo ln -sv /etc/sv/chronyd /var/service
    
    # LOCALE (e.g. US) =====
    # In /etc/locale.gen, uncomment:
    #   "en_US.UTF-8 UTF-8"
    #   and other locales. Then run:
    locale-gen
    # In /etc/locale.conf, set the LANG variable
    echo "LANG=en_US.UTF-8" > /etc/locale.conf
    

    sources: Gentoo § Installing base system § Timezone and Configure locales; Gentoo § Tools § Time synchronization; Void: Locales and Translations; Void: Date and Time

  • Build confs

    • Gentoo (/etc/portage/make.conf)

      • Required for graphics: VIDEO_CARDS

        # AMD: 'amdgpu radeonsi'
        # Nvidia: 'nouveau' or proprietary 'nvidia'
        # Generic: 'intel' or ancient 'intel i915'
        VIDEO_CARDS="intel nouveau"
        
      • Safe: COMMON_FLAG “-march=native” for CPU-specific compilation (source)

        # Resolve "-march=native" if using distcc
        gcc -v -E -x c /dev/null -o \
          /dev/null -march=native 2>&1 | \
          grep /cc1 | grep mtune
        
      • Safe: Parallel jobs in MAKEOPTS

        • Minimum between free -h --giga and nproc
      • Safe: Tell packages what kind of assembly code your system can use via CPU_FLAGS_<ARCH>

      • Less safe: Changing -O

      • Wrangling required: LTO

      • More involved: PGO (for package-by-package use)

        • Some binary packages use this upstream, such as Firefox.
      • Not recommended: directly hardening make.conf (source)

      • Optional: Change your “profile”. The default profile is based on the rootfs image used — changing may not be necessary.

        eselect profile list
        eselect profile set TARGET
        

        Profiles configure default USE flags, CFLAGS, etc. outside of make.conf.

      • Optional: ACCEPT_LICENSE

        • Don’t care about licensing? Use all of them:
          ACCEPT_LICENSE="*"
          
      • Required: Kernel

        • Often, manual kernel setups add support for rare situations or remove “unneeded” software (which isn’t loaded until you need it anyway).
        • Get firmware. The odd-licensed linux-firmware is needed for much hardware; sof-firmware depends on your chip (search your model as shown in cat /proc/cpuinfo). Microcode exists too.
          mkdir /etc/portage/package.license
          echo sys-kernel/linux-firmware @BINARY-REDISTRIBUTABLE \
            > /etc/portage/package.license/linux-firmware
          emerge --ask linux-firmware
          
        • Kernel method 1: gentoo-kernel-bin (depends on installkernel)
          # dracut and grub are common choices
          # to aid booting the kernel
          echo sys-kernel/installkernel dracut grub \
            > /etc/portage/package.use/installkernel
          emerge --ask gentoo-kernel-bin
          
        • Kernel method 2: genkernel
        • Kernel method 3: via Makefile (like true cavemen)
    • (optional) Arch users (/etc/makepkg.conf) should see ArchWiki: makepkg §3

      • §3.2 Consider “-march=native” in CFLAGS
      • §3.4.2 Consider multithreading zstd and/or reducing compression level
        COMPRESSZST=(zstd -c -T0 --auto-threads=logical -15 -)
        
      • §3.3.1 Consider compiling with more cores
        MAKEFLAGS="--jobs=$(nproc)"
        
      • §3.3.5 Consider disabling debug
  • Arch §3.5 network (NetworkManager)

    • $ echo "nice-hostname" > /etc/hostname
      # alternatively, after getting NetworkManager
      nmcli general hostname "nice-hostname"
      
    • Install and enable (Arch, Gentoo, Void) the NetworkManager daemon. Other network daemons may need to be disabled.
      • Gentoo: Consider enabling its highly common “dbus” use flag globally.
      • Daemons (services) are typically handled with:
  • Arch §3.7 users and root (sudo or doas)

    # Root user
    #   password should be very secure
    #   leave password empty to disable
    passwd
    # Normal user (joe)
    #   lowercase is a good idea
    useradd -m -G users,wheel,audio joe
    passwd joe
    #   use "groups" to check groups
    
    # Installing and using sudo?
    #   Uncomment "%sudo ALL=(ALL) ALL"
    #     Lets users in the sudo group use sudo.
    #     Using "%wheel ALL=(ALL) ALL" exists too
    EDITOR=nano visudo
    groupadd sudo # in case it isn't there
    usermod -aG sudo joe
    # Installing and using doas?
    echo "permit :wheel" > /etc/doas.conf
    chmod -c 0400 /etc/doas.conf
    
  • Arch §3.8 preparing to boot

6. configure after boot

Add a community repo (Gentoo), especially if you’re on Arch Linux.

# Arch: AUR (use `yay` or `paru`)
#   base-devel is the build essentials
#   NOTE: Always audit your makepkg files
pacman -S base-devel git
git clone https://aur.archlinux.org/yay
cd yay
makepkg -si
# or `makepkg -s` then `pacman -U <some.pkg.tar.zst>`

Consider using a package management help utility set such as pacman-contrib+pacutils or gentoolkit.

Afterwards, get a display working. Download a GUI (Xorg or a Wayland compositor).

6.5 fluffy tweaks

  • Make the CLI easier with bash-completions or zsh+ohmyzsh.
    • Arch: Add ILoveCandy and Color to /etc/pacman.conf options
      • Using paru? Uncomment BottomUp in /etc/paru.conf options
  • Set up shortcuts in your ~/.zshrc or ~/.bashrc:
    alias neofetch='fastfetch --config neofetch.jsonc'
    alias py=python
    tldr() {
      # e.g. `tldr emerge`
      curl "cht.sh/$1"
    }
    
  • Set up WINE or flatpaks or nix
  • Make the console beep noise shut up
  • Set envvars, e.g. for touchpads on Firefox
  • Install and enable the tlp power management daemon

further reading (annotated)

DenshiVideo. (2021, October 2). Gentoo: A comfy install guide [Video]. YouTube. https://www.youtube.com/watch?v=J7W9MItUSGw
This video guides a new user through the Gentoo AMD64 installation handbook from a live Linux Mint image using copy-pasted and explained commands, default configurations, and a binary kernel. It does not cover a desktop environment or system encryption, but this is a consequence of being faithful to the Gentoo developer-written installation handbook.

Leo3418. (2022, August 21). Gentoo configuration guide: Full disk LUKS2 with GRUB and systemd. Leo3418’s Personal Site. Retrieved August 9, 2024, from https://leo3418.github.io/collections/gentoo-config-luks2-grub-systemd/
Rather than settling for the pbkdf2 key derivation function, the user uses Portage to apply a GRUB2 argon2id patch, allowing LUKS2, GRUB2, and an encrypted /boot to coexist. Furthermore, GRUB is set up so that, given an incorrect password is entered at boot time, the password prompt reappears rather than entering GRUB rescue. There is also a discussion on LUKS unlock speed.

libreisaac. (2024, April 6). The best OS: Install Gentoo Linux on an encrypted Btrfs root with optional Sway WM. [Video]. YouTube. https://www.youtube.com/watch?v=t0nPxDlFL2I
This well-scoped primer covers how to replicate Isaac’s own hardened OpenRC Gentoo base installation on a given UEFI system from a Gentoo live image, featuring nested Btrfs, system-wide lto -O3 gcc optimizations, a compiled kernel, doas, and a SwayWM setup. However, some of his mount options are arguably redundant, such as defaults and discard=async; his wpa_cli rundown was flawed due to being in a VM (though he rectifies this in the transcript); his /etc/portage/env/no-lto file has redundancies; and the tutorial involves a git-cloning his Portage configuration (which includes “unsupported” settings such as PAX_MARKINGS) — even if he explains his options used.
Peculiarly, he mounts the EFI System Partition to both /boot and /efi to satisfy Gentoo-specific scripts that may expect /boot to be separate, or the ESP to be mounted at /efi instead of /boot.

Magyar, C. (2024, March 9). Arch Linux USB. Mags Zone. Retrieved June 27, 2024, from https://mags.zone/help/arch-usb.html
This article speedily installs Arch Linux from a live environment to a second USB, featuring minimal iwctl, a dual BIOS/UEFI GRUB setup, user and polkit configuration, arguably risky journalctl configurations, nomodeset for graphics cards, mkinitcpio optimizations, and traditional network interface naming (so that interface names do not differ between computers). The tutorial is notable for being endorsed by the ArchWiki.

Zhou, M., & Guō, Y. (2023, April 19). Arch Linux root on ZFS. OpenZFS. Retrieved July 2, 2024, from https://openzfs.github.io/openzfs-docs/Getting%20Started/Arch%20Linux/Root%20on%20ZFS.html
This article covers an Arch Linux rootfs-based UEFI install from a live Alpine image, using a parted script, rEFInd, and custom repo configurations. It is scoped to cover the bare minimum setup required to boot into an Arch ZFS system.