Arch Linux Installation Guide

Target system: Intel CPU laptop, 32 GB RAM, NVIDIA discrete GPU, UEFI firmware
Sources: Arch Wiki: Installation Guide, Btrfs, GRUB, Snapper, Zram, NVIDIA


Table of Contents

  1. Pre-Installation
  2. Disk Partitioning
  3. Format Partitions
  4. Create btrfs Subvolumes
  5. Mount Subvolumes
  6. Base System Installation
  7. System Configuration (chroot)
  8. mkinitcpio Configuration
  9. GRUB Bootloader
  10. Reboot
  11. zram Swap
  12. Snapper Configuration
  13. NVIDIA Drivers
  14. Intel iGPU
  15. Summary Checklist
  16. Caveats and Gotchas

1. Pre-Installation

Boot the Arch Linux live ISO. Then verify your environment:

# Verify UEFI mode (should return 64 or 32)
cat /sys/firmware/efi/fw_platform_size

# Verify network connectivity
ip addr
ping -c 3 archlinux.org

# Sync system clock
timedatectl set-ntp true

# List block devices to identify your disk
lsblk -f

Optionally update the mirror list for faster downloads:

reflector --verbose --protocol https --latest 10 --sort rate --save /etc/pacman.d/mirrorlist

2. Disk Partitioning

Use GPT partitioning (required for UEFI). Two partitions only — no swap partition (zram handles that).

Adjust /dev/nvme0n1 to match your disk (lsblk to confirm). NVMe partitions will be nvme0n1p1, nvme0n1p2; SATA will be sda1, sda2.

# Wipe existing partition table (DESTRUCTIVE)
wipefs -af /dev/nvme0n1
sgdisk --zap-all --clear /dev/nvme0n1

# EFI System Partition: 1 GB
sgdisk -n 1:1M:+1G   -t 1:ef00 -c 1:esp   /dev/nvme0n1

# Root partition: rest of the disk
sgdisk -n 2:0:0      -t 2:8300 -c 2:root  /dev/nvme0n1

partprobe /dev/nvme0n1
lsblk

3. Format Partitions

# EFI partition → FAT32
mkfs.fat -F 32 -n ESP /dev/nvme0n1p1

# Root partition → btrfs
mkfs.btrfs -L ROOT /dev/nvme0n1p2

4. Create btrfs Subvolumes

A well-structured subvolume layout keeps snapshots lean by excluding volatile data.

# Mount the top-level btrfs volume
mount /dev/nvme0n1p2 /mnt

# Create subvolumes
btrfs subvolume create /mnt/@           # /        (root)
btrfs subvolume create /mnt/@home       # /home
btrfs subvolume create /mnt/@snapshots  # /.snapshots  (for snapper)
btrfs subvolume create /mnt/@log        # /var/log
btrfs subvolume create /mnt/@pkg        # /var/cache/pacman/pkg
btrfs subvolume create /mnt/@tmp        # /tmp

# Verify
btrfs subvolume list /mnt

# Unmount top-level volume
umount /mnt

Subvolume design rationale:

  • @snapshots is separate so snapshots are not included within new snapshots (prevents recursive bloat)
  • @log, @pkg, @tmp are excluded from root snapshots — large/transient data doesn’t inflate snapshot diffs
  • /boot is not inside a btrfs subvolume — it lives on the FAT32 EFI partition

5. Mount Subvolumes

# Mount options (remove ssd/discard=async if using a spinning HDD)
OPTS="noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,commit=120"

# Mount root subvolume
mount -o ${OPTS},subvol=@ /dev/nvme0n1p2 /mnt

# Create mount point directories
mkdir -p /mnt/{boot,home,.snapshots,var/log,var/cache/pacman/pkg,tmp}

# Mount remaining subvolumes
mount -o ${OPTS},subvol=@home      /dev/nvme0n1p2 /mnt/home
mount -o ${OPTS},subvol=@snapshots /dev/nvme0n1p2 /mnt/.snapshots
mount -o ${OPTS},subvol=@log       /dev/nvme0n1p2 /mnt/var/log
mount -o ${OPTS},subvol=@pkg       /dev/nvme0n1p2 /mnt/var/cache/pacman/pkg
mount -o ${OPTS},subvol=@tmp       /dev/nvme0n1p2 /mnt/tmp

# Mount EFI partition
mount /dev/nvme0n1p1 /mnt/boot

# Verify mounts
df -h

Mount option notes:

Option Effect
noatime Skips access-time writes — reduces unnecessary write amplification
compress=zstd:3 Good balance of compression ratio and speed; use :1 for fast NVMe
ssd Enables SSD-optimized I/O patterns
discard=async Periodic TRIM (preferred over synchronous discard)
space_cache=v2 Improved free-space tracking
commit=120 Flush to disk every 120s (default 30s; trades durability for performance)

6. Base System Installation

# Install base system + all needed packages
pacstrap -K /mnt 
  base base-devel 
  linux linux-headers linux-firmware 
  btrfs-progs 
  intel-ucode 
  grub efibootmgr grub-btrfs inotify-tools 
  snapper snap-pac 
  networkmanager iwd 
  sudo nano vim 
  man-db man-pages 
  zram-generator

# Generate fstab
genfstab -U /mnt >> /mnt/etc/fstab

IMPORTANT: Review fstab and remove any subvolid= entries — keep only subvol=@name. This prevents rollbacks from failing (snapshot IDs differ from original subvolume IDs).

nano /mnt/etc/fstab
# Remove all subvolid=XXXX, occurrences from the mount options lines
cat /mnt/etc/fstab  # verify when done

Package notes:

  • intel-ucode — CPU microcode; GRUB loads it automatically after grub-mkconfig
  • grub-btrfs — adds btrfs snapshot entries to the GRUB menu
  • inotify-tools — required by grub-btrfsd to watch for new snapshots
  • snap-pac — pacman hook that auto-creates pre/post snapshots around every pacman transaction
  • iwd — Intel Wireless Daemon; used as NetworkManager’s WiFi backend (more reliable than wpa_supplicant on Intel hardware)
  • zram-generator — systemd-native zram swap; no manual service enablement needed

7. System Configuration (chroot)

arch-chroot /mnt

Timezone

ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
hwclock --systohc

Locale

nano /etc/locale.gen
# Uncomment your locale, e.g.: en_US.UTF-8 UTF-8

locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf

Console keymap (optional)

echo "KEYMAP=us" > /etc/vconsole.conf

Hostname

echo "yourhostname" > /etc/hostname

Hosts file

cat >> /etc/hosts << 'EOF'
127.0.0.1   localhost
::1         localhost
127.0.1.1   yourhostname.localdomain yourhostname
EOF

Root password and user account

passwd

useradd -m -G wheel -s /bin/bash yourusername
passwd yourusername

# Allow wheel group sudo access
EDITOR=nano visudo
# Uncomment:  %wheel ALL=(ALL:ALL) ALL

Enable NetworkManager and iwd

systemctl enable NetworkManager
systemctl enable iwd

# Configure NetworkManager to use iwd as the WiFi backend
mkdir -p /etc/NetworkManager/conf.d
cat > /etc/NetworkManager/conf.d/wifi_backend.conf << 'EOF'
[device]
wifi.backend=iwd
EOF

8. mkinitcpio Configuration

Edit /etc/mkinitcpio.conf:

nano /etc/mkinitcpio.conf

Set MODULES and HOOKS as follows:

# btrfs only — do NOT add nvidia modules here, they are not installed yet
# nvidia modules will be added in step 13 after driver installation
MODULES=(btrfs)

# Remove 'kms' to prevent nouveau from loading before the proprietary NVIDIA driver
HOOKS=(base udev autodetect microcode modconf block filesystems keyboard fsck)

Critical: Remove kms from HOOKS now. Do not add nvidia modules to MODULES yet — the drivers are not installed until step 13, and mkinitcpio -P will fail if you reference modules that don’t exist. The nvidia modules are added in step 13 before driver installation.

Regenerate the initramfs:

mkinitcpio -P

9. GRUB Bootloader

Install GRUB to EFI

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB

Configure GRUB

nano /etc/default/grub

Modify or add these settings:

# Do NOT add nvidia-drm parameters here — drivers are not installed yet
# They will be added in step 13 after driver installation
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

# Allow recovery/snapshot boot entries in the GRUB menu
GRUB_DISABLE_RECOVERY=false

Generate GRUB config

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

grub-mkconfig auto-detects intel-ucode and adds /boot/intel-ucode.img as an initrd entry. No manual microcode config needed. The nvidia DRM kernel parameters are added in step 13.


10. Exit and Reboot

exit             # exit chroot
umount -R /mnt
reboot

Remove the installation media when prompted. Boot into your new system and log in as root (or your user).


11. Post-Boot: zram Swap

No swap partition is needed. Configure zram via zram-generator:

sudo nano /etc/systemd/zram-generator.conf
[zram0]
zram-size = min(ram / 2, 16384)
compression-algorithm = zstd
swap-priority = 100
fs-type = swap

For 32 GB RAM: ram / 2 = 16384 MB, creating a 16 GB compressed zram swap device. zstd provides excellent compression ratios with minimal CPU overhead.

Apply and verify:

sudo systemctl daemon-reload

# Manually start the zram device (daemon-reload alone does not activate it)
sudo systemctl start systemd-zram-setup@zram0.service

# Verify zram is active
zramctl
swapon --show

If zramctl shows no output, check for errors: systemctl status systemd-zram-setup@zram0.service

No service needs to be enabled — zram-generator integrates with systemd’s device unit generator and activates automatically on every subsequent boot.


12. Post-Boot: Snapper Configuration

The /.snapshots subvolume conflict

Snapper’s create-config tries to create a new /.snapshots btrfs subvolume, but you already have @snapshots mounted there. The steps below must be followed in order — skipping the unmount causes both create-config and btrfs subvolume delete to fail.

# 1. REQUIRED FIRST: unmount /.snapshots before running snapper
#    Skipping this causes all subsequent steps to fail
sudo umount /.snapshots

# Confirm it is unmounted
findmnt /.snapshots   # should return nothing

# 2. Remove the empty directory left inside @ after unmounting
#    (mkdir created it during install; btrfs subvolume create fails if path exists)
sudo rmdir /.snapshots

# 3. Create snapper config — this creates a NEW /.snapshots subvolume inside @
sudo snapper -c root create-config /

# Confirm snapper created a subvolume (required before the delete will work)
sudo btrfs subvolume show /.snapshots

# 4. Delete the subvolume snapper just created
sudo btrfs subvolume delete /.snapshots

# 5. Recreate the directory and remount your @snapshots subvolume
sudo mkdir /.snapshots
sudo mount -a   # remounts all fstab entries including @snapshots

# 6. Set permissions
sudo chmod 750 /.snapshots

# Verify
findmnt /.snapshots
sudo snapper list-configs

Enable automatic snapshot timers

sudo systemctl enable --now snapper-timeline.timer  # hourly/daily/weekly timeline snapshots
sudo systemctl enable --now snapper-cleanup.timer   # automatic cleanup of old snapshots
sudo systemctl enable --now snapper-boot.timer      # snapshot at each boot

Tune snapshot retention

sudo nano /etc/snapper/configs/root
TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
NUMBER_LIMIT="10"
NUMBER_LIMIT_IMPORTANT="10"
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="0"
TIMELINE_LIMIT_YEARLY="0"

Enable grub-btrfsd (auto-update GRUB snapshot menu)

sudo systemctl enable --now grub-btrfsd

# Verify
systemctl status grub-btrfsd

# Manually regenerate GRUB snapshot entries at any time:
sudo grub-mkconfig -o /boot/grub/grub.cfg

snap-pac (pacman pre/post hooks)

Already active — no configuration needed. Verify:

ls /usr/share/libalpm/hooks/ | grep snap
# Should show: snap-pac-pre.hook  snap-pac-post.hook

Every pacman -S, -R, -Syu etc. will automatically create a snapshot pair.

Enable btrfs quotas (required for cleanup limits to work)

sudo btrfs quota enable /

Common snapper commands

sudo snapper -c root list                             # list all snapshots
sudo snapper -c root create --description "pre-change"  # manual snapshot
sudo snapper -c root diff 1..2                        # diff between snapshots 1 and 2
sudo snapper -c root undochange 1..2                  # revert file changes between snapshots
sudo snapper -c root rollback 5                       # roll back to snapshot 5 (then reboot)
sudo snapper -c root delete 3                         # delete snapshot 3

13. NVIDIA Proprietary Drivers

Identify your GPU

lspci -k | grep -A 2 -E "(VGA|3D)"

Choose the right driver package

GPU Generation Kernel Package
Turing (RTX 20xx) and newer linux nvidia-open (Nvidia’s open kernel module)
Maxwell through Ada Lovelace linux nvidia
Any of the above LTS/custom kernel nvidia-open-dkms or nvidia-dkms

Step 1 — Add nvidia modules to mkinitcpio

Do this before installing the drivers so the pacman hook generates a correct initramfs on first install:

sudo nano /etc/mkinitcpio.conf
# Add nvidia modules now that the drivers are about to be installed
MODULES=(btrfs nvidia nvidia_modeset nvidia_uvm nvidia_drm)

# kms must still be absent from HOOKS
HOOKS=(base udev autodetect microcode modconf block filesystems keyboard fsck)

Step 2 — Create the pacman hook

This ensures mkinitcpio -P runs automatically on every nvidia or kernel update:

sudo mkdir -p /etc/pacman.d/hooks
sudo nano /etc/pacman.d/hooks/nvidia.hook
[Trigger]
Operation=Install
Operation=Upgrade
Operation=Remove
Type=Package
Target=nvidia
Target=nvidia-open
Target=nvidia-dkms
Target=nvidia-open-dkms
Target=linux

[Action]
Description=Update NVIDIA module in initramfs
When=PostTransaction
Exec=/usr/bin/mkinitcpio -P

Step 3 — Install drivers

The pacman hook runs mkinitcpio -P automatically at the end of installation:

# Enable multilib for 32-bit library support (needed for Steam, Wine, etc.)
sudo nano /etc/pacman.conf
# Uncomment the [multilib] section and the Include line below it
sudo pacman -Syu

# Install drivers (choose one line based on your GPU):
sudo pacman -S nvidia-open nvidia-utils lib32-nvidia-utils nvidia-settings   # Turing+
# sudo pacman -S nvidia nvidia-utils lib32-nvidia-utils nvidia-settings       # Maxwell-Ada

# mkinitcpio -P is run automatically by the pacman hook above — no need to run it manually

Step 4 — Add nvidia parameters to GRUB

Now that the drivers are installed, add the DRM modesetting kernel parameters:

sudo nano /etc/default/grub
# Enable DRM modesetting — required for Wayland and proper nvidia KMS
# nvidia-drm.fbdev=1 is required on kernel 6.2+
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nvidia-drm.modeset=1 nvidia-drm.fbdev=1"
# Regenerate GRUB config to apply the new parameters
sudo grub-mkconfig -o /boot/grub/grub.cfg

Verify after reboot

# DRM modesetting should be active
cat /sys/module/nvidia_drm/parameters/modeset
# Expected: Y

# Check driver is loaded
nvidia-smi

NVIDIA + Wayland notes

  • egl-wayland is required (usually auto-installed as a dependency of nvidia-utils)
  • nvidia-drm.modeset=1 in GRUB kernel parameters is mandatory for Wayland
  • GNOME and KDE Plasma auto-enable Wayland when DRM modesetting is active
  • For Hyprland, add to your config: env = LIBVA_DRIVER_NAME,nvidia env = GBM_BACKEND,nvidia-drm env = __GLX_VENDOR_LIBRARY_NAME,nvidia

14. Intel Graphics (iGPU)

The Intel iGPU is used for the display and hardware video decoding. No separate Xorg driver config is needed for modern kernels.

# Mesa (OpenGL/Vulkan for Intel iGPU)
sudo pacman -S mesa lib32-mesa

# Vulkan support (Gen 5 Broadwell and newer)
sudo pacman -S vulkan-intel lib32-vulkan-intel vulkan-icd-loader lib32-vulkan-icd-loader

# VA-API hardware video acceleration
# Gen 8+ (Broadwell+): use intel-media-driver (iHD backend — recommended)
sudo pacman -S intel-media-driver libva-utils

# Gen 7 and older only: use libva-intel-driver instead
# sudo pacman -S libva-intel-driver

Set the VA-API driver (if not auto-detected):

echo "LIBVA_DRIVER_NAME=iHD" | sudo tee -a /etc/environment

Verify hardware acceleration

On a laptop with both Intel iGPU and NVIDIA dGPU there will be two render nodes — renderD128 and renderD129. vainfo without flags only works once a display server (Wayland/X11) is running. From a TTY use the DRM backend and target the Intel render node directly.

First, identify which render node belongs to the Intel iGPU:

ls -la /dev/dri/by-path/
# Look for the entry ending in -render whose PCI address matches the Intel GPU
# Intel iGPU is always at PCI bus 00:02.0, so look for:
# pci-0000:00:02.0-render -> ../renderD128  (or renderD129)

Then run vainfo against that node:

# Try renderD128 first (Intel is usually here, but confirm with by-path above)
LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128

# If that returns "vaInitialize failed", try renderD129 instead
LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD129

A working result lists supported decode/encode profiles. Once a desktop environment is running, vainfo alone will work without any flags.


15. Summary Checklist

Step Action
UEFI check cat /sys/firmware/efi/fw_platform_size → 32 or 64
Partition sgdisk: EFI (ef00, 1G) + root (8300, rest of disk)
Format mkfs.fat -F 32 for EFI; mkfs.btrfs for root
btrfs subvolumes @, @home, @snapshots, @log, @pkg, @tmp
Mount options noatime,compress=zstd:3,ssd,discard=async,space_cache=v2
pacstrap Include intel-ucode grub efibootmgr grub-btrfs inotify-tools snapper snap-pac zram-generator
fstab genfstab -U; remove all subvolid= entries
mkinitcpio MODULES (step 8) btrfs only — nvidia modules added later in step 13
mkinitcpio HOOKS Remove kms; keep microcode
GRUB install grub-install --target=x86_64-efi --efi-directory=/boot
GRUB cmdline (step 9) quiet splash only — nvidia params added in step 13
zram /etc/systemd/zram-generator.conf with zram-size = min(ram/2, 16384)
Snapper create-config /; delete snapper’s /.snapshots; remount @snapshots
grub-btrfsd systemctl enable --now grub-btrfsd
Snapper timers snapper-timeline.timer, snapper-cleanup.timer, snapper-boot.timer
btrfs quotas btrfs quota enable /
NVIDIA (step 13) 1. Add nvidia to MODULES; 2. create hook; 3. install drivers; 4. update GRUB cmdline
Intel iGPU mesa vulkan-intel intel-media-driver
Verify microcode journalctl -k --grep=microcode
Verify NVIDIA DRM cat /sys/module/nvidia_drm/parameters/modesetY

16. Key Caveats and Gotchas

  1. subvolid= in fstab — Always remove subvolid= from fstab; keep only subvol=@name. Snapshot IDs differ from original subvolume IDs, so rollbacks will fail if subvolid is hardcoded.

  2. Snapper + /.snapshots conflict — Snapper’s create-config creates its own /.snapshots subvolume. Delete it and remount your pre-created @snapshots subvolume (step 12).

  3. mkinitcpio nvidia modules — two-phase setup — Do not add nvidia modules to MODULES=() during the chroot (step 8); the drivers do not exist yet and mkinitcpio -P will fail. Add them in step 13 before running pacman -S nvidia-open. Also remove the kms hook in step 8 to prevent nouveau from loading.

  4. NVIDIA GRUB parameters — add after drivers — Do not add nvidia-drm.modeset=1 or nvidia-drm.fbdev=1 to GRUB_CMDLINE_LINUX_DEFAULT during the chroot (step 9). Add them in step 13 after installing the drivers, then run grub-mkconfig. If you reboot without these parameters set, Wayland may not work but the system will still boot.

  5. NVIDIA driver update hook — The pacman hook at /etc/pacman.d/hooks/nvidia.hook ensures mkinitcpio -P runs on every nvidia or linux package update. Without this, a kernel update can silently break NVIDIA.

  6. grub-btrfs snapshot pathgrub-btrfsd watches /.snapshots by default. If you change the snapshot directory, edit /etc/default/grub-btrfs/config accordingly.

  7. /boot on FAT32 — In this guide, /boot is the EFI partition (FAT32) and kernel images live there. GRUB reads this reliably. Placing /boot inside the btrfs root is possible but more complex.

  8. btrfs RAID — GRUB supports btrfs RAID 0/1/10 but not RAID 5/6. Do not use RAID 5/6 if you intend to boot from btrfs.

  9. snap-pac and quota cleanup — Snapper’s automatic cleanup count limits require btrfs quotas enabled: btrfs quota enable /. There is some overhead on very large filesystems.

  10. zram vs swap partition — With 32 GB RAM, a 16 GB zram device gives generous compressed swap at zero disk cost. The zram-generator requires no service enablement — systemd handles activation at boot via its unit generator.

~$ chm0d
// execute permission on knowledge
© 2026 chm0d.com — all rights reserved. built on arch, btw  //  EOF
Scroll to Top