Pop!_OS 24.04: installation guide with BTRFS, LUKS encryption and auto snapshots with Timeshift
Please feel free to raise any comments or issues on the website’s Github repository. Pull requests are very much appreciated.

Note that this written guide is an updated version of the video and contains much more information.
Table of Contents
Overview
In this guide, I will show you how to install Pop!_OS 24.04 with the following structure:
- An unencrypted EFI partition for the systemd bootloader.
- An unencrypted partition for the Pop!_OS recovery system.
- An encrypted swap partition (note: hibernation is not supported by default).
- An encrypted BTRFS partition (with LVM) for the root filesystem.
- The BTRFS logical volume contains a subvolume
@for/and a subvolume@homefor/home.
- The BTRFS logical volume contains a subvolume
- Automatic system snapshots and easy rollback using Timeshift, which will regularly take (almost instant) snapshots of the system.
This setup works similarly well on other distributions, for which I also have installation guides (with optional RAID1).
Step 0: General remarks
This tutorial uses Pop!_OS 24.04 from System76, installed via a USB flash device. Other versions of Pop!_OS and distributions using the systemd boot manager might also work but may require additional steps (see my other installation guides).
Step 1: Prepare partitions by performing a Clean Install first
To maintain the default partition layout of Pop!_OS,1 the easiest and quickest approach is to perform the installation twice:
- Run the automatic
Clean Installwith encryption. This creates the LUKS-encrypted LVM volume group with a logical volume calledrootcontaining the actual system files formatted with ext4. - Perform a second installation using the
Custom (Advanced)option. This allows us to select and format the partitions as needed. While we can customize the partitions, I prefer to keep them close to the defaults and only change the filesystem to BTRFS.
First, run the automatic Clean Install with encryption.
Restart Device. Instead, right-click the Install Pop!_OS app in the dock and select Quit.
If you want to understand the structure of the installation, keep reading. Otherwise, proceed to the next step to perform the second installation.
[Optional] Understand the default partition layout and installation structure
Open a terminal and examine the default partition layout (alternatively, use Gparted):
sudo lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
# loop0 7:0 0 2.4G 1 loop /rofs
# sda 8:0 0 3.6T 0 disk
# sdb 8:16 0 3.6T 0 disk
# sdc 8:32 0 119.2G 0 disk
# ├─sdc1 8:33 0 1022M 0 part
# ├─sdc2 8:34 0 4G 0 part
# ├─sdc3 8:35 0 110.2G 0 part
# └─sdc4 8:36 0 4G 0 part
# sdd 8:48 1 0B 0 disk
# sde 8:64 1 0B 0 disk
# sdf 8:80 1 0B 0 disk
# sdg 8:96 1 0B 0 disk
# sdh 8:112 1 29.3G 0 disk
# ├─sdh1 8:113 1 2.8G 0 part /cdrom
# ├─sdh2 8:114 1 4M 0 part
# └─sdh3 8:115 1 26.5G 0 part /var/crash
# /var/log
# sdi 8:128 0 1.4T 0 disk
# sr0 11:0 1 1024M 0 rom
# sr1 11:1 1 1024M 0 rom
# sr2 11:2 1 1024M 0 rom
# sr3 11:3 1 1024M 0 rom
# zram0 251:0 0 16G 0 disk [SWAP]
# nvme0n1 259:0 0 1.7T 0 disk
# ├─nvme0n1p1 259:1 0 16M 0 part
# ├─nvme0n1p2 259:2 0 99.6G 0 part
# ├─nvme0n1p3 259:3 0 9.8G 0 part
# └─nvme0n1p4 259:4 0 1.6T 0 part
I have installed Pop!_OS on the 119.2 GB SSD, recognized as sdc on my machine. Note that I have several other disks connected, so we must identify the correct disk carefully.
Let’s look closer at the partition layout of sdc:
sudo parted /dev/sdc unit MiB print
# Model: ATA ThinkSystem M.2 (scsi)
# Disk /dev/sdc: 122040MiB
# Sector size (logical/physical): 512B/512B
# Partition Table: gpt
# Disk Flags:
#
# Number Start End Size File system Name Flags
# 1 2.00MiB 1024MiB 1022MiB fat32 boot, esp
# 2 1024MiB 5120MiB 4096MiB fat32 recovery msftdata
# 3 5120MiB 117942MiB 112822MiB
# 4 117942MiB 122038MiB 4096MiB linux-swap(v1) swap
We have the following 4 partitions:
- A 1022 MiB FAT32 EFI partition for the systemd bootloader (note the
boot, espflag). - A 4096 MiB FAT32 partition for the Pop!_OS recovery system (note the name
recovery). - A 112822 MiB partition containing the actual system files.
- A 4096 MiB swap partition for (encrypted) swap use (note the
swapflag).
Let’s examine the encrypted sdc3 partition:
sudo cryptsetup luksDump /dev/sdc3
# LUKS header information
# Version: 2
# Epoch: 3
# Metadata area: 16384 [bytes]
# Keyslots area: 16744448 [bytes]
# UUID: 0be45abc-4d9f-4682-9a51-17432c068ef2
# Label: (no label)
# Subsystem: (no subsystem)
# Flags: (no flags)
#
# Data segments:
# 0: crypt
# offset: 16777216 [bytes]
# length: (whole device)
# cipher: aes-xts-plain64
# sector: 512 [bytes]
This confirms the partition is encrypted with LUKS using default options. Now, let’s see what is inside the encrypted partition:
sudo cryptsetup luksOpen /dev/sdc3 cryptdata
# Enter passphrase for /dev/sdc3:
ls /dev/mapper
# control cryptdata data-root
sudo pvs
# PV VG Fmt Attr PSize PFree
# /dev/mapper/cryptdata data lvm2 a-- 110.16g 0
sudo vgs
# VG #PV #LV #SN Attr VSize VFree
# data 1 1 0 wz--n- 110.16g 0
sudo lvs
# LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
# root data -wi-a----- 110.16g
sudo lsblk /dev/mapper/data-root -f
# NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
# data-root ext4 1.0 2e9d6dac-1ee4-419c-b21c-8212262fdb93
By default, when installing with encryption, Pop!_OS uses Logical Volume Management (LVM). The encrypted partition (cryptdata) is a physical volume containing a volume group called data. Inside this volume group, there is a logical volume called root containing our system files, formatted with ext4.
While I don’t use LVM features extensively, the installer requires LVM for encryption, and there is practically no performance downside.
Now that we know the partition layout, let’s close everything:
sudo cryptsetup luksClose /dev/mapper/data-root
sudo cryptsetup luksClose /dev/mapper/cryptdata
ls /dev/mapper
# control
We are ready to proceed with the second install, which follows the same steps but uses BTRFS as the underlying filesystem.
Step 2: Install Pop!_OS using the Custom (Advanced) option
Open Install Pop!_OS from Applications again, and select the region, language, and keyboard layout. Then choose Custom (Advanced).
You will see your partitioned hard disk (for me, /dev/sdc) as shown in the previous step:
- First partition: Click it, activate
Use partition, activateFormat, set Use as toBoot /boot/efi, and Filesystem tofat32. - Second partition: Click it, activate
Use partition, activateFormat, set Use as toCustomand enter/recovery, set Filesystem tofat32. - Fourth partition: Click it, activate
Use partition, set Use as toSwap. - Third (largest) partition: Click it. A
Decrypt This Partitiondialog opens; enter your LUKS password and hitDecrypt. A new device calledLVM datawill appear at the bottom of the screen (you might need to scroll down). Click on this partition, activateUse partition, activateFormat, set Use as toRoot (/), and Filesystem tobtrfs.
If you have other partitions, check their types and usage. Ensure you deactivate using or changing any other EFI partitions.
Recheck everything (ensure the correct use of partitions that have a black checkmark) and hit Erase and Install.
Follow the steps to create a user account and write changes to the disk.
Restart Device, but keep the window open.
Step 3: Configure BTRFS subvolumes and mount options
Open a terminal and switch to an interactive root session:
sudo -i
Maximizing the terminal window helps when working with the command line.
Mount the BTRFS top-level root filesystem with zstd compression
Mount the root partition (the top-level BTRFS volume always has id 5) with compression enabled to optimize performance and durability:
cryptsetup luksOpen /dev/sdc3 cryptdata
# Enter passphrase for /dev/sdc3
mount -o subvolid=5,defaults,compress=zstd:1 /dev/mapper/data-root /mnt
By default, Pop!_OS 24.04 uses standard BTRFS mount options. SSDs are automatically detected (ssd mount option), and space_cache=v2 is used (also the default in Fedora).
There is some debate about using noatime instead of relatime. I haven’t seen a significant difference, so I use the default relatime.
However, I recommend using compress=zstd:1 (specifically level 1) for SSDs, as recommended by the Fedora team for workstations.
discard=async: Since Linux kernel 6.2, BTRFS enables discard=async by default on devices that support TRIM operations. Pop!_OS 24.04 ships with a 6.x kernel, so there is no need to explicitly add discard=async to the mount options—it is automatically applied. If you need to disable it for some reason, use the nodiscard option.
We will add these mount options to fstab later, but it’s good practice to use compression now when moving system files into the subvolumes.
Create BTRFS subvolumes @ and @home
First, create the subvolume @:
btrfs subvolume create /mnt/@
# Create subvolume '/mnt/@'
Next, move all files and folders from the top-level filesystem into @:
cd /mnt
ls | grep -v @ | xargs mv -t @
ls -a /mnt
# . .. @
Since we mounted the drive with compression enabled, the data is automatically compressed as it is moved.
Now, create the @home subvolume and move the user folder from /mnt/@/home/ into it:
btrfs subvolume create /mnt/@home
# Create subvolume '/mnt/@home'
mv /mnt/@/home/* /mnt/@home/
Verify that everything has been moved correctly:
ls -a /mnt/@/home
# . ..
ls -a /mnt/@home
# . .. wmutschl
btrfs subvolume list /mnt
# ID 256 gen 17 top level 5 path @
# ID 257 gen 17 top level 5 path @home
Changes to fstab
We need to update /etc/fstab to:
- Mount
/to the@subvolume. - Mount
/hometo the@homesubvolume. - Enable compression.
Open fstab with a text editor (e.g., nano /mnt/@/etc/fstab) or use these sed commands:
sed -i 's/btrfs defaults 0 1/btrfs defaults,compress=zstd:1,subvol=@ 0 0/' /mnt/@/etc/fstab
echo "UUID=$(blkid -s UUID -o value /dev/mapper/data-root) /home btrfs defaults,compress=zstd:1,subvol=@home 0 0" >> /mnt/@/etc/fstab
Your fstab should look like this:
cat /mnt/@/etc/fstab
# PARTUUID=2a345427-756a-454d-943b-6028c9e5cf55 /boot/efi vfat umask=0077 0 0
# PARTUUID=526a5925-7e0f-44ff-9a69-da10eb0f1c5c /recovery vfat umask=0077 0 0
# /dev/mapper/cryptswap none swap defaults 0 0
# UUID=f4190602-7a0b-4d6c-aed5-624a17afaf8f / btrfs defaults,compress=zstd:1,subvol=@ 0 0
# UUID=f4190602-7a0b-4d6c-aed5-624a17afaf8f /home btrfs defaults,compress=zstd:1,subvol=@home 0 0
Note that your UUIDs will be different. The last two lines are the important ones.
discard in crypttab: You may notice that Pop!_OS does not add a discard option to /etc/crypttab for the LUKS-encrypted partition. This is intentional for security reasons: enabling discard on encrypted devices can expose patterns about which blocks are unused, potentially leaking metadata about the encrypted content. Pop!_OS prioritizes security over the marginal performance benefit. If you prefer to enable TRIM passthrough for performance reasons and accept this trade-off, you can manually add discard to the crypttab entry for cryptdata, see my guide for POP!_OS 22.04.
Adjust configuration of kernelstub
We need to adjust settings for the systemd boot manager and ensure they persist after kernel updates. Add rootflags=subvol=@ to the "user" kernel options in the kernelstub configuration file:
nano /mnt/@/etc/kernelstub/configuration
Your configuration file should look like this (add the rootflags=subvol=@ line to the user section):
cat /mnt/@/etc/kernelstub/configuration
# {
# "default": {
# "kernel_options": [
# "quiet",
# "splash"
# ],
# "esp_path": "/boot/efi",
# "setup_loader": false,
# "manage_mode": false,
# "force_update": false,
# "live_mode": false,
# "config_rev": 3
# },
# "user": {
# "kernel_options": [
# "quiet",
# "loglevel=0",
# "systemd.show_status=false",
# "splash",
# "rootflags=subvol=@",
# ""
# ],
# "esp_path": "/boot/efi",
# "setup_loader": true,
# "manage_mode": true,
# "force_update": false,
# "live_mode": false,
# "config_rev": 3
# }
# }
update-initramfs will fail later!
Alternatively, you can use this sed command to apply the change:
sed -i '/\"user\"/,/}/{ s/\(\"splash\",\)/\1\n \"rootflags=subvol=@\",/ }' /mnt/@/etc/kernelstub/configuration
Adjust configuration of systemd bootloader
Mount the EFI partition to adjust systemd boot settings:
mount /dev/sdc1 /mnt/@/boot/efi
Add rootflags=subvol=@ to the last line of Pop_OS_current.conf:
sed -i 's/splash/splash rootflags=subvol=@/' /mnt/@/boot/efi/loader/entries/Pop_OS-current.conf
cat /mnt/@/boot/efi/loader/entries/Pop_OS-current.conf
# title Pop!_OS
# linux /EFI/Pop_OS-f4190602-7a0b-4d6c-aed5-624a17afaf8f/vmlinuz.efi
# initrd /EFI/Pop_OS-f4190602-7a0b-4d6c-aed5-624a17afaf8f/initrd.img
# options root=UUID=f4190602-7a0b-4d6c-aed5-624a17afaf8f ro quiet loglevel=0 systemd.show_status=false splash rootflags=subvol=@
Optionally, add a timeout to the systemd boot menu to easily access the recovery partition:
echo "timeout 3" >> /mnt/@/boot/efi/loader/loader.conf
cat /mnt/@/boot/efi/loader/loader.conf
# default Pop_OS-current
# timeout 3
Create a chroot environment and update initramfs
To work inside the newly installed OS without booting into it, create a chroot environment.
Unmount the top-level root filesystem (id 5) and remount the subvolume @ to /mnt:
cd /
umount -l /mnt
mount -o subvol=@,defaults,compress=zstd:1 /dev/mapper/data-root /mnt
ls /mnt
# bin bin.usr-is-merged boot dev etc home lib lib.usr-is-merged lib64 media mnt opt proc recovery root run sbin sbin.usr-is-merged srv sys tmp usr var
Mount the necessary system directories and enter chroot (commands from System76’s Repair the Bootloader guide):
for i in /dev /dev/pts /proc /sys /run; do mount -B $i /mnt$i; done
chroot /mnt
Now inside the new system, verify the fstab mounts:
mount -av
# mount: (hint) your fstab has been modified, but systemd still uses
# the old version; use 'systemctl daemon-reload' to reload.
# /boot/efi : successfully mounted
# /recovery : successfully mounted
# none : ignored
# / : ignored
# /home : successfully mounted
Update the initramfs to include the kernelstub changes:
update-initramfs -c -k all
# update-initramfs: Generating /boot/initrd.img-6.16.3-76061603-generic
# W: /sbin/fsck.btrfs doesn't exist, can't install to initramfs
# mount: (hint) your fstab has been modified, but systemd still uses
# the old version; use 'systemctl daemon-reload' to reload.
# Updating kernel version 6.16.3-76061603-generic in systemd-boot...
# kernelstub.Config : INFO Looking for configuration...
# kernelstub : INFO System information:
#
# OS:..................Pop!_OS 24.04
# Root partition:....../dev/dm-1
# Root FS UUID:........f4190602-7a0b-4d6c-aed5-624a17afaf8f
# ESP Path:............/boot/efi
# ESP Partition:......./dev/sdc1
# ESP Partition #:.....1
# NVRAM entry #:.......-1
# Boot Variable #:.....0000
# Kernel Boot Options:.quiet loglevel=0 systemd.show_status=false splash rootflags=subvol=@
# Kernel Image Path:.../boot/vmlinuz-6.16.3-76061603-generic
# Initrd Image Path:.../boot/initrd.img-6.16.3-76061603-generic
# Force-overwrite:.....False
#
# kernelstub.Installer : INFO Copying Kernel into ESP
# kernelstub.Installer : INFO Copying initrd.img into ESP
# kernelstub.Installer : INFO Setting up loader.conf configuration
# kernelstub.Installer : INFO Making entry file for Pop!_OS
# kernelstub.Installer : INFO Backing up old kernel
# kernelstub.Installer : INFO No old kernel found, skipping
If you see errors, check that you added the comma in the /etc/kernelstub/configuration file.
Note the warning about /sbin/fsck.btrfs, so let’s install btrfs-progs to be able to manage BTRFS subvolumes and fix the issue (this triggers another update-initramfs):
sudo apt install -y btrfs-progs
Exit the chroot:
exit
Step 4: Reboot, some checks, and system updates
Close the terminal and click Reboot Device on the installer app.
Crossing fingers!
If all goes well, you will see a passphrase prompt. Enter your LUKS passphrase, and your system should boot.
After the welcome screen, we go to settings and:
- Network & Wireless: check your connection, for example deactivate unused interfaces and add IP configuration to used interface or add Wifi password.
- Power & Battery: Use
High performanceinPower Modeand checkPower Saving Options(on a server I deactivate them, on a notebook I keep them).
Next, we verify the setup in a terminal:
sudo mount -av
# /boot/efi : already mounted
# /recovery : already mounted
# none : ignored
# / : ignored
# /home : already mounted
Check that the optimized mount options are active (note that you cannot have different mount options on the same partition):
sudo mount -v | grep /dev/mapper
# /dev/mapper/data-root on / type btrfs (rw,relatime,compress=zstd:1,space_cache=v2,subvolid=256,subvol=/@)
# /dev/mapper/data-root on /home type btrfs (rw,relatime,compress=zstd:1,space_cache=v2,subvolid=257,subvol=/@home)
Verify swap is working:
Run sudo swapon to check if both the swap partition and the zram device are active:
sudo swapon
# NAME TYPE SIZE USED PRIO
# /dev/dm-2 partition 4G 0B -2
# /dev/zram0 partition 16G 0B 1000
Pop!_OS uses two swap mechanisms:
- zram (compressed RAM swap): A high-priority swap device that compresses data in RAM. This is used first due to its higher priority (1000) and provides fast swap performance for everyday use.
- Swap partition (encrypted): A lower-priority disk-based swap partition used when zram is full.
About Hibernation: Hibernation (suspend-to-disk) is not officially supported on Pop!_OS. According to System76’s documentation, there are several reasons why hibernation is not enabled by default:
- Non-persistent swap encryption key: The swap partition is encrypted with a random key generated at each boot (see the
cryptswapentry in/etc/crypttab). This means any data written to swap—including hibernation data—cannot be read after a reboot, making hibernation impossible without reconfiguration. - Zram incompatibility: Zram exists only in volatile RAM and cannot persist hibernation data across power cycles.
- Default partition layout: The default 4GB swap partition may be insufficient for systems with more RAM (hibernation requires swap ≥ RAM size).
- SSD wear: Hibernation adds significant write traffic to SSDs (equal to total RAM each time), potentially shortening drive lifespan.
If you need hibernation, System76 provides a guide to enable it, which involves removing the default swap partition, creating a persistent encrypted swap volume inside LVM, and configuring the kernel resume parameter. This is beyond the scope of this guide.
Inspect BTRFS subvolumes:
sudo btrfs filesystem show /
# Label: none uuid: f4190602-7a0b-4d6c-aed5-624a17afaf8f
# Total devices 1 FS bytes used 7.91GiB
# devid 1 size 110.16GiB used 12.02GiB path /dev/mapper/data-root
sudo btrfs subvolume list /
# ID 256 gen 96 top level 5 path @
# ID 257 gen 96 top level 5 path @home
If using an SSD or NVMe, enable fstrim.timer:
sudo systemctl enable fstrim.timer
Make sure issue_discards=1 is set in /etc/lvm/lvm.conf (usually default):
cat /etc/lvm/lvm.conf | grep issue_discards
# # Configuration option devices/issue_discards.
# issue_discards = 1
Finally, update the system including the recovery partition and flatpak applications:
sudo apt update
sudo apt upgrade
sudo apt dist-upgrade
sudo apt autoremove --purge
sudo apt autoclean
pop-upgrade recovery upgrade from-release
flatpak -vv update
I also recommend updating firmware:
sudo fwupdmgr get-devices
sudo fwupdmgr get-updates
sudo fwupdmgr update
Reboot once more:
sudo reboot now
Step 5: Snapshot tools
With BTRFS set up with @ and @home subvolumes, you can now use various snapshot and backup tools:
- Timeshift: A popular GUI tool for system snapshots. Note that Timeshift requires the
@and@homesubvolume naming convention we set up in this guide. - grub-btrfs: Automatically adds GRUB menu entries for your snapshots, allowing you to boot directly into any snapshot.
- Btrbk: A powerful tool for snapshot management and backup with support for incremental send/receive to remote locations. Personally, I prefer Btrbk for its flexibility and send/receive capabilities.
- Snapper: Another snapshot management tool, popular in openSUSE.
We will now focus only on Timeshift as it is (in my opinion) the most user-friendly one.
Install and configure Timeshift
Install Timeshift and configure it via the GUI:
sudo apt update
sudo apt install -y timeshift
Go to Applications and search for Timeshift.
- Select BTRFS as the Snapshot Type; click Next.
- Select your BTRFS system partition as the Snapshot Location; click Next. Ignore the warning “Selected device does not have BTRFS partition”-this is a bug with encrypted devices.
- Select Snapshot Levels (my recommendations):
- Activate Monthly and set to 2.
- Activate Weekly and set to 3.
- Activate Daily and set to 5.
- Deactivate Hourly.
- Activate Boot and set to 5.
- Activate Stop cron emails for scheduled tasks.
- Click Next.
- User Home Directories: Include the
@homesubvolume (not selected by default). While you usually restore only@, having home backups is convenient. - Click Finish.
- Create a manual snapshot named “Clean Install” and exit Timeshift.
Timeshift will now manage snapshots automatically.
Verify snapshots
In a terminal, you can verify the snapshots:
sudo btrfs subvolume list /
# ID 256 gen 160 top level 5 path @
# ID 257 gen 157 top level 5 path @home
# ID 258 gen 153 top level 5 path timeshift-btrfs/snapshots/2025-12-09_10-37-12/@
# ID 259 gen 153 top level 5 path timeshift-btrfs/snapshots/2025-12-09_10-37-12/@home
Step 6: Practice recovery and system rollback
Let’s simulate a system failure to practice recovery. Note: We have snapshots to rollback to (in the subvolumes at the top-level root).
Delete the /etc folder (simulating disaster):
sudo rm -rf /etc
If you try to reboot, the system will fail. So instead, boot into the Pop!_OS Recovery System selected from the systemd bootloader (this is why I set the timeout to 3 seconds).
I will show you two ways to rollback: with Timeshift and manually. For both, first open the File Manager, go to Other Locations, select your encrypted disk, and enter the passphrase. This mounts the top-level root and you can now access the snapshots.
Rollback with Timeshift
- Install Timeshift in the live environment via the terminal:
sudo apt update sudo apt install timeshift - Go to
Applicationsand open Timeshift. - Select BTRFS, and choose your disk. Ignore the warning “Selected device does not have BTRFS partition”-this is a bug with encrypted devices.
- Select a snapshot and click Restore.
- Timeshift will restore the system subvolume. Reboot, and your system should be back to normal.
Rollback manually
- Open a terminal and navigate to the mounted volume (e.g.,
/media/recovery/<YOUR-UUID>):sudo -i cd /media/recovery/<YOUR-UUID> ls # @ @home timeshift-btrfs - Move the broken subvolume:
sudo mv @ @.broken - Find a valid snapshot in
timeshift-btrfs/snapshots/:ls timeshift-btrfs/snapshots - Create a snapshot of the good subvolume as the new
@(this will also make the read-only subvolume writable):sudo btrfs subvolume snapshot timeshift-btrfs/snapshots/2025-12-09_09-59-43/@ @ - Reboot.
Once back in your system, delete the broken subvolume to save space:
sudo btrfs subvolume list /
sudo btrfs subvolume delete @.broken
FINISHED! CONGRATULATIONS!
Check out my Pop!_OS post-installation steps.
-
If you already have (a previous version of) Pop!_OS installed, you can safely skip this step as you already have a partition layout that works with the installer. ↩︎