[HowTo] Enable and configure hibernation with BTRFS

Difficulty: ★★★☆☆

  1. Create the swapfile:

    export size=<number>; \
    cd / && \
    touch swapfile \
    truncate -s 0 ./swapfile \
    chattr +C ./swapfile \
    btrfs property set ./swapfile compression none \
    dd if=/dev/zero of=/swapfile bs=1M count=$size status=progress \
    chmod 0600 swapfile \
    mkswap swapfile
    
  2. Add an entry and enable the swapfile in /etc/fstab

    /swapfile    none    swap    sw    0    0
    

    and

    swapon /swapfile && swapon -s
    
  3. Enable hibernation:

    Add source /etc/default/grub.d/*.conf on the BOTTOM of the file /etc/default/grub and create the directory /etc/default/grub.d/.

    Create a file which you name resume.conf in /etc/default/grub.d/ and fill it with:

    GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT resume=UUID=$uuid resume_offset=$offset"
    

    You get the UUID from:

    findmnt -no UUID -T /swapfile
    

    and the OFFSET from:

    filefrag -v /swapfile | awk '{ if($1=="0:"){print substr($4, 1, length($4)-2)} }'
    

    Then add resume after filesystems with space between to the HOOKS= variable in /etc/mkinitcpio.conf like this:

    HOOKS="base udev autodetect modconf block keyboard keymap filesystems resume"
    

    Now we need to bypass the memory check because systemd does not support it on btrfs.

    Edit the services with:

    sudo systemctl edit systemd-logind.service
    

    and

    sudo systemctl edit systemd-hibernate.service
    

    and add there:

    Environment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1

  4. If everything has been made, then update grub and the initramfs:

    grub-mkconfig -o /boot/grub/grub.cfg && mkinitcpio -P
    

Test it with sudo systemctl hibernate after you reboot once.

Success! :wink:

Here is a little script, that could make things easier. I used it myself without problems.
Updated & tested 16-11-2021

configuration script
#!/bin/bash
exec >2&

# Calculate required swap size
ram=$(grep "MemTotal" /proc/meminfo | tr -d " " | sed -r "s/(\w+):([0-9]+)(\w+)/\\2/g") 
size=$(printf "%.0f\n" $(echo "$ram * 1.5 / 1000" | bc ))

# Configure swapfile on btrfs nested subvolume to maintain TimeShift compatibility
# Disable current swap, remove its entries from fstab, mount fstab, remove swap folder and file
swapoff /swap/swapfile
sed -i"-backup" '/swap/d' /etc/fstab
mount -a
rm -rf /swap/swapfile
rm -rf /swap
# Configure swap for BTRFS with hibernate support
btrfs subvolume create /@swap
touch /@swap/swapfile
truncate -s 0 /@swap/swapfile
chattr +C /@swap/swapfile
btrfs property set /@swap/swapfile compression none
dd if=/dev/zero of=/@swap/swapfile bs=1M count=$size status=progress
chmod 0600 /@swap/swapfile
mkswap /@swap/swapfile
echo -e '# Swapfile for hibernation support, on nested subvolume\n/@swap/swapfile\tnone\tswap\tsw\t0\t0' >> /etc/fstab
[[ -z $(swapon -s | grep "/@swap/swapfile") ]] && swapon /@swap/swapfile

# Enable Hibernation
# pm-utils should be there for legacy support, but since Manjaro uses systemd, it would be obsolete (will be removed after clearly verified)  
# pacman -S --noconfirm pm-utils
offset=$(filefrag -v /@swap/swapfile | awk '{ if($1=="0:"){print substr($4, 1, length($4)-2)} }')
uuid=$(findmnt -no UUID -T /@swap/swapfile)
mkdir -p /etc/default/grub.d/

if [[ -z $(grep "source /etc/default/grub.d/*" /etc/default/grub) ]]
	then
		echo -e "\n\nsource /etc/default/grub.d/*" >> /etc/default/grub  
fi
echo -e "# Added by a script\nGRUB_CMDLINE_LINUX_DEFAULT=\"\$GRUB_CMDLINE_LINUX_DEFAULT resume=UUID=$uuid resume_offset=$offset\"" > /etc/default/grub.d/resume.cfg

if [[ -z $(grep "resume" /etc/mkinitcpio.conf) ]]
	then
		sed -i "s/filesystems/filesystems resume/g" /etc/mkinitcpio.conf 
fi

# Due no support for btrfs we need to bypass the memory check
mkdir -p /etc/systemd/system/systemd-logind.service.d/		
mkdir -p /etc/systemd/system/systemd-hibernate.service.d/		
logind="/etc/systemd/system/systemd-logind.service.d/bypass_hibernation_memory_check.conf"
hibernate="/etc/systemd/system/systemd-hibernate.service.d/bypass_hibernation_memory_check.conf"

if [[ ! -f $logind ]]
	then
		echo -e "#Added by a script\nEnvironment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1" \
		>  /etc/systemd/system/systemd-logind.service.d/bypass_hibernation_memory_check.conf
fi

if [[ ! -f $hibernate ]]
	then
		echo -e "#Added by a script\nEnvironment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1" \
		> /etc/systemd/system/systemd-hibernate.service.d/bypass_hibernation_memory_check.conf
fi
grub-mkconfig -o /boot/grub/grub.cfg
mkinitcpio -P

sed -i -e 's@#HibernateDelaySec=180min@HibernateDelaySec=60min@g' /etc/systemd/sleep.conf

:warning: This is a wiki article. You are free to copy, share, or edit the content without restrictions as long as it doesn’t miss the main topic.

7 Likes

As far as I remember, swapfile should be created on a special subvolume with CoW disabled.

2 Likes

amazing!
thank you, it works! :smiley:

I have been looking for weeks to get this working, see my previous ramblings about the topic here:

(I even wasted several hours to try to setup an Arch installation, but failed miserable :wink: )

If I understood it correctly, CoW can only be disabled globally for the whole btrfs file system or for single files as done by the command:
chattr +C ./swapfile

The nodatacow option is ignored for subvolume mounts, if other subvolumes of the same btrfs filesystem have been mounted before without it. (at least that’s what I read here: [SOLVED] BTRFS: nodatacow on subvolume / Newbie Corner / Arch Linux Forums )

The only thing missing is creating this file on a seperate subvolume, in order for things like Timeshift to work correctly. You do not want to include a huge swapfile in snapshots and backups.

Simply add btrfs subvolume create /@swap to the beginning of your script and replace /swapfile for /@swap/swapfile in your script should do it. I will try it out later.
This also corresponds with the Manjaro wiki. It is a requirement for compatibility:
Swap - Manjaro

Also, when you run this script after installing Manjaro and you had already selected “swapfile” during setup, the script deletes the existing swapfile first. All good, but should you not first do swapoff -a otherwise the file is probably locked/in use? Also, should you not first unmount the swapfile?

@megavolt your script misses a closing bracket on line 54.
Also, my /etc/fstab contains the following commented line:
# Created by a script\n/swapfile\tnone\tswap\tsw\t0\t0 nothing below this comment.
The script should have added:
/swapfile none swap sw 0 0
In my case:
/@swap/swapfile none swap sw 0 0

1 Like

Hello @zilexa :wink:

I added a note so that it makes clear that you can edit the article at any time:

So if you think that a subvolume should be used, then feel free to change or add it there.

@megavolt I updated & tested the script, works fine now (tested on Manjaro Gnome).

It will now first disable swap, backup fstab and remove any lines with “swap” in fstab before creating btrfs swap properly.

However, I do have 1 question: Why do you need to install pm-utils ?
It won’t install when you use the script because it conflicts with tlp. But after running the script (and verifying pm-utils is indeed not installed and tlp is still installed, hibernation works fine?

1 Like

TLP and pm-utils share some symlinks and therefore it conflicts, but I see that I actually use systemd on all systems… FS#38692 : TLP and pm-utils are now conflicts

I will comment it out and remove it after it is clearly verified :wink:

1 Like