Arch Linux Installation Guide: btrfs + GRUB + NVIDIA

// arch-linux

Arch Linux Installation Guide: btrfs + GRUB + NVIDIA

Intel laptop | btrfs subvolumes | Snapper snapshots | zram | NVIDIA proprietary drivers

# Arch Linux Installation Guide

Intel Laptop | btrfs + GRUB | Snapper Snapshots | zram | NVIDIA

Target system: Intel CPU laptop, 32 GB RAM, NVIDIA discrete GPU, UEFI firmware
Sources: [Arch Wiki: Installation Guide](https://wiki.archlinux.org/title/Installation_guide), [Btrfs](https://wiki.archlinux.org/title/Btrfs), [GRUB](https://wiki.archlinux.org/title/GRUB), [Snapper](https://wiki.archlinux.org/title/Snapper), [Zram](https://wiki.archlinux.org/title/Zram), [NVIDIA](https://wiki.archlinux.org/title/NVIDIA)

Table of Contents

1. [Pre-Installation](#1-pre-installation) 2. [Disk Partitioning](#2-disk-partitioning) 3. [Format Partitions](#3-format-partitions) 4. [Create btrfs Subvolumes](#4-create-btrfs-subvolumes) 5. [Mount Subvolumes](#5-mount-subvolumes) 6. [Base System Installation](#6-base-system-installation) 7. [System Configuration (chroot)](#7-system-configuration-chroot) 8. [mkinitcpio Configuration](#8-mkinitcpio-configuration) 9. [GRUB Bootloader](#9-grub-bootloader) 10. [Reboot](#10-exit-and-reboot) 11. [zram Swap](#11-post-boot-zram-swap) 12. [Snapper Configuration](#12-post-boot-snapper-configuration) 13. [NVIDIA Drivers](#13-nvidia-proprietary-drivers) 14. [Intel iGPU](#14-intel-graphics-igpu) 15. [Summary Checklist](#15-summary-checklist) 16. [Caveats and Gotchas](#16-key-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-mkconfiggrub-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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top