Hibernation (also known as suspend to disk) is one of the many features which most Linux distributions do not officially support. Suspending to disk is not an easy feature to implement and involves certain risks. The process starts at the kernel.
For a system to enter the hibernation state, the kernel sleeps all active processes pausing the OS and dumping the RAM to non-volatile memory and finally goes into the power-off state where a complete power loss does not result in the loss of data. On restart, it might be slower to exit the state as the memory content is loaded back into RAM before the computer completely starts.
I am working on Pop OS 22.04 with a custom partition structure using BTRFS filesystem and no encrypted partition. While this entry is focused on Pop OS, other Linux-based OS, Ubuntu and Ubuntu-based distributions might find some of its content relevant.
This entry assumes the reader already has a BTRFS system with no swap partition or file.
Enabling suspend to disk in Pop OS requires the following steps:
- Disable the default Zram
- Create a swap sub-volume and swapfile
- Update kernel stubs
- Test and enable hibernation
There are some limitations to the implementations of Linux swap in BTRFS . A few of these require that the filesystem must be only a single device and have only a single data profile, no copy-on-write, no compression, etc.
# Disable the default Zram
Pop OS now has Zram enabled by default. With this module enabled, the kernel can create a RAM device with active disk compression of files and or inactive memory pages. The RAM device can be used for swap or as a disk for storage of temporary files.
For hibernation, Zram is not a suitable option. The module may be used for swap in RAM to speed up operation, however, contents in the RAM are volatile and do not persist after a power loss.
# Remove Pop OS Zram settings sudo apt purge pop-default-settings-zram # Reboot the system sudo reboot now
# Create swap sub-volume and swapfile
# The sub-volume
BTRFS sub-volumes are mountable file trees (not block devices or logical volumes) with support for more than a single file tree on different path levels. The filesystem has a top-level sub-volume with an ID of 5. For the swap sub-volume to be created on this level, the root sub-volume needs to be mounted with the target sub-volume ID 5.
# List block devices lsblk -o "NAME,UUID,SIZE,TYPE,MOUNTPOINTS"
$ lsblk -o "NAME,UUID,SIZE,TYPE,MOUNTPOINTS" NAME UUID SIZE TYPE MOUNTPOINTS nvme0n1 953.9G disk ├─nvme0n1p1 8132-6E65 2G part /boot/efi ├─nvme0n1p2 128M part ├─nvme0n1p3 4C747256747242AE 736.2G part ├─nvme0n1p4 5A900223900205EB 990M part ├─nvme0n1p5 DE40025D40023CB1 17.8G part ├─nvme0n1p6 42C6987BC6987141 1.4G part ├─nvme0n1p7 269C-2883 4G part /recovery └─nvme0n1p8 21d80ba7-c82e-44f6-9ad6-1c815922ac27 191.3G part /home /
Your drive will have different names and layouts. The current device is NVME drive and uses a PCI driver, hence the device names begin with
nvme. SCSI/PATA/SATA/USB devices may have different names similar to
sda1, et cetera.
The target partition is
nvme0n1p8 which is the BTRFS partition with the root and home sub-volumes mounted.
# Mount the root level sudo mount -o subvolid=5,ssd,noatime,space_cache=v2,commit=10,compress=zstd,autodefrag /dev/nvme0n1p8 /mnt # Create a @swap sub-volume sudo btrfs subvolume create /mnt/@swap # List sub-volumes on the root level sudo btrfs subvolume list /mnt
The mount options used above should be adjusted per use case. For instance, depending on the version of BTRFS progs, the
ssd may not be required as BTRFS will enable or disable SSD optimisations based on the disk type . The swap sub-volume will be created on the root level. Listing of the BTRFS sub-volumes will show the new
ID 256 gen 102182 top level 5 path @ ID 257 gen 102182 top level 5 path @home + ID 258 gen 100589 top level 5 path @swap
# The swapfile
The size of the swap file is proportional to the size of the system RAM. For suspending to disk, we need a swapfile bigger than the RAM.
free -h shows the system memory information.
$ free -h total used free shared buff/cache available Mem: 31Gi 11Gi 14Gi 1.6Gi 5.0Gi 16Gi
Based on the output, the total memory on our system is
31Gi, hence a swapfile of
33Gi is enough to accommodate the contents of the RAM at hibernation.
This step of the process requires an updated version of BTRFS, at least version 6.1 and above. The version provides easy commands to make a swapfile and to retrieve the
sudo btrfs filesystem mkswapfile --size 33G /mnt/@swap/swapfile
Or manually create the swapfile.
sudo truncate -s 0 /mnt/@swap/swapfile sudo chattr +C /mnt/@swap/swapfile sudo fallocate -l 33G /mnt/@swap/swapfile sudo chmod 0600 /mnt/@swap/swapfile sudo mkswap /mnt/@swap/swapfile
# Enable swap
Update mount entries in
/etc/fstab to include the swap sub-volume and the swap file.
# Unmount the root level sub-volume sudo umount /mnt # Set a variable to hold the target partition UUID UUID="$(lsblk --noheadings -o UUID /dev/nvme0n1p8)" # Update fstab entries sudo tee -a /etc/fstab << EOF UUID=$UUID /swap btrfs defaults,subvol=@swap,nodatacow,noatime,nospace_cache,compress=no 0 0 /swap/swapfile none swap defaults 0 0 EOF # Mount all entries including the @swap sub-volume sudo mount -av # Activate swapfile sudo swapon /swap/swapfile # Show system memory and swap free -h
The new output of
free should include a row with the swap information.
$ free -h total used free shared buff/cache available Mem: 31Gi 11Gi 13Gi 1.6Gi 5.8Gi 16Gi Swap: 32Gi 0B 32Gi
# Update kernel stubs
With the swapfile now available, it only serves to store inactive memory pages when the system is running out of memory. Adding options which can be passed to the kernel at boot time will ensure that the system is resumed after hibernation.
resume option specifies with disk partition and
resume_offset points to the exact location of the swapfile. The
resume_offset is required when using a swapfile, otherwise, only the
resume is required for a swap partition.
The following command shows the swapfile offset.
$ sudo btrfs inspect-internal map-swapfile /swap/swapfile Physical start: 112232574976 Resume offset: 27400531
# Set a variable to hold the offset value OFFSET="$(sudo btrfs inspect-internal map-swapfile -r /swap/swapfile)" # Ensure the target partition UUID is still set echo $UUID # Optional: Remove any resume and resume_offset options if they already exist # Replace <UUID> and <OFFSET> with their values sudo kernelstub -d 'resume=UUID=<UUID>' 'resume_offset=<OFFSET>' # Add options if they are not present sudo kernelstub -a "resume=UUID=$UUID resume_offset=$OFFSET" # Show current configuration sudo kernelstub -p # Update initramfs config echo "resume=UUID=$UUID resume_offset=$OFFSET" | sudo tee /etc/initramfs-tools/conf.d/resume sudo echo $OFFSET > /sys/power/resume_offset # Update existing initramfs sudo update-initramfs -u
# Test and enable hibernation
Ensure hibernation works by executing the following command.
And if there was an issue.
Check the system logs for error messages. Any of the following commands should provide additional information about the problem.
sudo journalctl -r -u systemd-hibernate.service sudo journalctl -r -u hibernate.target sudo systemctl list-dependencies -a hibernate.target
Common error messages include:
- No such device: Check that you have the correct kernel options. Ensure the
resume_offsetoptions are correct.
- No space left on device: The swapfile or swap partition does not have enough space. Your swap space may be less than the RAM or there are multiple swap spaces with at least one not having enough space.
Add Hibernate and Hybrid Sleep options to the power menu, using Hibernate Status Button GNOME-Shell extension.
Ultimately, add the following policy kit.
sudo tee /etc/polkit-1/localauthority/10-vendor.d/com.ubuntu.desktop.pkla << EOF [Enable hibernate in upower] Identity=unix-user:* Action=org.freedesktop.upower.hibernate ResultActive=yes [Enable hibernate in logind] Identity=unix-user:* Action=org.freedesktop.login1.hibernate;org.freedesktop.login1.handle-hibernate-key;org.freedesktop.login1;org.freedesktop.login1.hibernate-multiple-sessions;org.freedesktop.login1.hibernate-ignore-inhibit ResultActive=yes EOF