Skip to content

Instantly share code, notes, and snippets.

@djallits
Last active February 14, 2024 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djallits/4e54ed961efb1838b991fab11b6050e9 to your computer and use it in GitHub Desktop.
Save djallits/4e54ed961efb1838b991fab11b6050e9 to your computer and use it in GitHub Desktop.
Arch Linux From Scratch: An Installation Guide

Arch Linux from Scratch

This guide was written for my everyday driver (described below):

  • Dell XPS 15 9500
    • 10th Generation Intel® Core™ i9-10885H (16MB Cache, up to 5.3 GHz, 16 cores)
    • NVIDIA® GeForce® GTX 1650 Ti 4GB GDDR6
    • 64GB DDR4-2933MHz, 2x32G
    • 2TB M.2 PCIe NVMe Solid State Drive
    • 15.6" UHD+ (3840 x 2400) InfinityEdge Touch Anti-Reflective 500-Nit Display
    • Killer Wi-fi 6 AX500-DBS (2x2) and Bluetooth 5.1

This guide was built to do the following:

Known Issues

  • The Qualcomm 6390 (Killer Wi-fi 6 AX500-DBS) can be troublesome to get both Wi-Fi and Bluetooth working in parallel.

Pre-Installation

Complete steps 1.1 trough 1.4 of the Arch Linux Installation Guide. This takes you through the process of acquiring an installation image, verifying its signature, placing the image on an installation medium, and booting into the live environment.

$ nvme format /dev/nvme0n1 --force --ses 1
$ cryptsetup open /dev/nvme0n1 --type plain --key-file /dev/urandom  to-be-wiped
$ dd if=/dev/zero of=/dev/mapper/to-be-wiped  bs=1M status=progress
$ cryptsetup close to-be-wiped
$ sgdisk --zap-all /dev/nvme0n1
$ sgdisk --clear
$ sgdisk --new=1:0:+1GB --typecode=1:ef00 --change-name=1:UEFI /dev/nvme0n1
$ sgdisk --new=2:0:0 --typecode=2:e800 --change-name=2:ARCH /dev/nvme0n1
$ cryptsetup luksFormat --type luks2 --hash sha512 --cipher aes-xts-plain64 --key-size 512 /dev/disk/by-partlabel/ARCH
$ cryptsetup open /dev/disk/by-partlabel/ARCH arch
$ pvcreate /dev/mapper/arch
$ vgcreate volgrp00 /dev/mapper/arch
$ lvcreate --size 65590108K volgrp00 --name swap
$ lvcreate --extents 100%FREE volgrp00 --name arch
$ mkswap --label SWAP /dev/volgrp00/swap
$ swapon /dev/volgrp00/swap
$ mkfs.fat -F32 -n UEFI /dev/disk/by-partlabel/UEFI
$ mkfs.btrfs --label ARCH /dev/volgrp00/arch
$ mount /dev/volgrp00/arch /mnt
$ mkdir /mnt/boot
$ btrfs subvolume create /mnt/@
$ btrfs subvolume create /mnt/@/.snapshots
$ btrfs subvolume create /mnt/@/opt
$ btrfs subvolume create /mnt/@/root
$ btrfs subvolume create /mnt/@/srv
$ mkdir /mnt/@/usr/
$ btrfs subvolume create /mnt/@/usr/local
$ mkdir -p /mnt/@/var/cache/pacman
$ btrfs subvolume create /mnt/@/var/cache/pacman/pkg
$ mkdir -p /mnt/@/var/lib/libvirt
$ btrfs subvolume create/mnt/@/var/lib/containers
$ btrfs subvolume create/mnt/@/var/lib/libvirt/images
$ btrfs subvolume create/mnt/@/var/lib/machines
$ btrfs subvolume create /mnt/@/var/log
$ btrfs subvolume create /mnt/@/var/spool
$ btrfs subvolume create /mnt/@/var/tmp

Disable Copy on Write for the following subvolumes.

$ chattr +C /mnt/@/var/cache/pacman/pkg
$ chattr +C /mnt/@/var/lib/containers
$ chattr +C /mnt/@/var/lib/libvirt/images
$ chattr +C /mnt/@/var/lib/machines
$ chattr +C /mnt/@/var/log
$ chattr +C /mnt/@/var/spool
$ chattr +C /mnt/@/var/tmp
$ umount /mnt
$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@ \
          /dev/volgrp00/arch \
          /mnt

$ mount -t vfat /dev/nvme0n1p1  /mnt/boot

$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/.snapshots \
          /dev/volgrp00/arch \
          /mnt/.snapshots
$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/home/djallits \
          /dev/volgrp00/arch \
          /mnt/home/djallits
$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/opt \
          /dev/volgrp00/arch \
          /mnt/opt
$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/root \
          /dev/volgrp00/arch \
          /mnt/root
$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/srv \
          /dev/volgrp00/arch \
          /mnt/srv

$ mount -o defaults,noatime,commit=120,compress=zstd,discard=async,space_cache=v2,ssd,subvol=@/usr/local \
          /dev/volgrp00/arch \
          /mnt/usr/local
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/cache/cache/pacman/pkg \
          /dev/volgrp00/arch \
          /mnt/var/cache/pacman/pkg
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/lib/containers \
          /dev/volgrp00/arch \
          /mnt/var/lib/containers
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/lib/libvirt/images \
          /dev/volgrp00/arch \
          /mnt/var/lib/libvirt/images
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/lib/machines \
          /dev/volgrp00/arch \
          /mnt/var/lib/machines
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/log \
          /dev/volgrp00/arch \
          /mnt/var/log
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/spool \
          /dev/volgrp00/arch \
          /mnt/var/spool
$ mount -o defaults,noatime,commit=120,discard=async,nodatacow,space_cache=v2,ssd,subvol=@/var/tmp \
          /dev/volgrp00/arch \
          /mnt/var/tmp

Connect to the Internet...

If you do not have a wired connection, you will need to establish a wireless connection.

$ iwctl station wlan0 connect {{ WIFI_SSID }}

Validate a successful Internet Connection...

$ ping archlinux.org

Linux System Installation

Install the essential packages...

$ pacstrap /mnt base base-devel btrfs-progs dkms fwupd git gnome gnome-tweaks intel-ucode linux linux-firmware \
                linux-firmware-bnx2x linux-firmware-liquidio linux-firmware-mellanox linux-firmware-nfp \
                linux-firmware-qlogic linux-lts lvm2 man-db man-pages neofetch networkmanager pacman-contrib \
                pipewire plymouth reflector sbctl snapper snap-pac texinfo tree vim wireplumber wget zsh

Generate /mnt/etc/fstab...

$ genfstab -U /mnt >> /mnt/etc/fstab

Linux System Configuration

chroot into...

$ arch-chroot /mnt

Configure time...

$ ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime
$ hwclock --systohc --utc

Edit /etc/locale.gen...

$ vim /etc/locale.gen

#en_SG ISO-8859-1
en_US.UTF-8 UTF-8
#en_US ISO-8859-1

Generate locales...

$ locale-gen

Edit /etc/locale.conf...

$ vim /etc/locale.conf

LANG=en_US.UTF-8
LC_ADDRESS="C.UTF-8"
LC_ALL=
LC_COLLATE="C.UTF-8"
LC_CTYPE="C.UTF-8"
LC_IDENTIFICATION="C.UTF-8"
LC_MEASUREMENT="C.UTF-8"
LC_MESSAGES=
LC_MONETARY="C.UTF-8"
LC_NAME="C.UTF-8"
LC_NUMERIC="C.UTF-8"
LC_PAPER="C.UTF-8"
LC_TELEPHONE="C.UTF-8"
LC_TIME="C.UTF-8"

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/locale.conf >> /etc/locale.conf

Edit /etc/hostname...

$ vim /etc/hostname

{{ HOST_NAME }}

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/hostname >> /etc/hostname

Edit /etc/hosts...

$ vim /etc/hosts

# Static table lookup for hostnames.
# See hosts(5) for details.

127.0.0.1 djallits-xps-15-9500.localdomain djallits-xps-15-9500
127.0.0.1 localhost.localdomain localhost

::1       localhost ip6-localhost ip6-loopback
fe00::0   ip6-localnet
ff00::0   ip6-mcastprefix
ff02::1   ip6-allnodes
ff02::2   ip6-allrouters
ff02::3   ip6-allhosts

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/hosts >> /etc/hosts

Edit /etc/vconsole.conf...

$ vim /etc/vconsole.conf

KEYMAP=us

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/vconsole.conf >> /etc/vconsole.conf

Create your everyday user account...

$ useradd --btrfs-subvolume-home --create-home --gid users --groups docker wheel --shell /usr/bin/zsh {{ USER_NAME }}
$ passwd {{ USER_NAME }}

Optionally enable sudo access to the wheel group...

$ EDITOR=vim visudo

...
## Uncomment to allow members of group wheel to execute any command
%wheel ALL=(ALL:ALL) NOPASSWD: ALL
...

Or better yet...

$ vim /etc/sudoers.d/{{ USER_NAME }}

{{ USER_NAME }} ALL=(ALL:ALL) NOPASSWD: ALL

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/djallits >> /etc/sudoers.d/djallits

Configure Reflector to overwrite the local mirrorlist with the five most recently synchronized mirrors in the United States sorted by download speed over HTTPS.

$ vim /etc/xdg/reflector/reflector.conf

--country United States
--latest 5
--protocol https
--sort rate
--save /etc/pacman.d/mirrorlist

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/be9d985339d2350fe9b01a253aae123b81c481aa/reflector.conf >> /etc/xdg/reflector/reflector.conf

Set Spinner as the Default Plymouth theme

$ plymouth-set-default-theme -R spinner

Configure Linux kernel options...

$ vim /etc/kernel/cmdline

loglevel=3 nowatchdog quiet rd.luks.name=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=ARCH resume=/dev/volgrp00/swap root=/dev/volgrp00/arch rootflags=subvol=@ rw splash

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/bd9a8cb503974c6b3733fe8e50bb4b69ebaf57b3/cmdline >> /etc/kernel/cmdline

Where XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is the UUID of /dev/nvme0n1p2. Use the following in VIM to get the UUID.

:read ! blkid -s UUID -o value /dev/nvme0n1p2

Configure NTP

$ vim /etc/systemd/timesyncd.conf

...
[Time]
NTP=time.google.com
FallbackNTP=0.us.pool.ntp.org 1.us.pool.ntp.org 2.us.pool.ntp.org 3.us.pool.ntp.org
...

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/bd9a8cb503974c6b3733fe8e50bb4b69ebaf57b3/timesyncd.conf >> /etc/systemd/timesyncd.conf

Install and Configure Boot Manager...

$ bootctl --path=/boot install

Configure initramfs...

$ vim /etc/mkinitcpio.conf

...
HOOKS=(systemd plymouth autodetect modconf kms block keyboard sd-vconsole sd-encrypt lvm2 filesystems fsck)
...
COMPRESSION="lz4"
...

Note To do this faster...

$ curl -K https://gist.githubusercontent.com/djallits/4e54ed961efb1838b991fab11b6050e9/raw/bd9a8cb503974c6b3733fe8e50bb4b69ebaf57b3/mkinitcpio.conf >> /etc/mkinitcpio.conf

Now we generate our initial ramdisk for both the linux and linux-lts kernels.

$ mkinitcpio -P

Enable systemd services...

$ systemctl enable gdm.service
$ systemctl enable NetworkManager
$ systemctl enable reflector.service
$ systemctl enable reflector.timer
$ systemctl enable systemd-resolved.service
$ systemctl enable systemd-timesyncd.service

$ systemctl mask systemd-networkd.service

Exit from chroot...

$ exit

Recursively unmount from /mnt...

$ umount -R /mnt

Moment of truth, reboot...

$ systemctl reboot --firmware-setup

First Steps

Let's install yay, which is a Pacman wrapper and AUR helper written in Go.

$ cd ~/Downloads
$ git clone https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg -si
$ cd .. && rm -rf ~/Downloads/*

Now let's install all the fun stuff!

$ yay -S aic94xx-firmware albert ast-firmware aur-auto-vote-git btrfs-assistant git-extras google-chrome \
         mkinitcpio-firmware nerd-fonts-git oh-my-zsh-git paccache-hook spaceship-prompt spotify teams \
         upd72020x-fw visual-studio-code-bin wd719x-firmware yay zoom 

Customize Gnome

After installing the gnome-browser-connector with yay, head on over to https://extensions.gnome.org and customize your Gnome Desktop.

(1) Make sure to enable Night Light and use the Sunset to Sunrise schedule in Settings -> Displays.

loglevel=3 nowatchdog quiet rd.luks.name=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=ARCH resume=/dev/volgrp00/swap root=/dev/volgrp00/arch rw splash
djallits ALL=(ALL:ALL) NOPASSWD: ALL
djallits-xps-15-9500
# Static table lookup for hostnames.
# See hosts(5) for details.
127.0.0.1 djallits-xps-15-9500.localdomain djallits-xps-15-9500
127.0.0.1 localhost.localdomain localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
LANG=en_US.UTF-8
LC_ADDRESS="C.UTF-8"
LC_ALL=
LC_COLLATE="C.UTF-8"
LC_CTYPE="C.UTF-8"
LC_IDENTIFICATION="C.UTF-8"
LC_MEASUREMENT="C.UTF-8"
LC_MESSAGES=
LC_MONETARY="C.UTF-8"
LC_NAME="C.UTF-8"
LC_NUMERIC="C.UTF-8"
LC_PAPER="C.UTF-8"
LC_TELEPHONE="C.UTF-8"
LC_TIME="C.UTF-8"
MODULES=()
BINARIES=()
FILES=()
HOOKS=(systemd plymouth autodetect modconf kms block keyboard sd-vconsole sd-encrypt lvm2 filesystems fsck)
COMPRESSION="lz4"
COMPRESSION_OPTIONS=()
--country United States
--latest 5
--protocol https
--sort rate
--save /etc/pacman.d/mirrorlist
[Time]
NTP=time.google.com
FallbackNTP=0.us.pool.ntp.org 1.us.pool.ntp.org 2.us.pool.ntp.org 3.us.pool.ntp.org
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment