// 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/nvme0n1to match your disk (lsblkto confirm). NVMe partitions will benvme0n1p1,nvme0n1p2; SATA will besda1,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: Removekmsfrom HOOKS now. Do not add nvidia modules toMODULESyet — the drivers are not installed until step 13, andmkinitcpio -Pwill 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-mkconfigauto-detectsintel-ucodeand adds/boot/intel-ucode.imgas 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
Ifzramctlshows 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/modeset → Y |
---
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 path — grub-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.
// categories
// recent posts