← blog

Hibernating a Laptop on Linux is a Chore

Invalid DateTime

Setting up hibernation on Linux is still way too hard. Here's how I did it on my Framework laptop, so that I could leave it for a few days with the lid shut and still have battery left! Prior art at the links below, but here's a step by step.

Fedora Magazine: Update on Hibernation in Fedora Workstation xoid.net: Configuring Hibernate on Linux Laptop

Steps to Enable

  1. Create a btrfs subvolume containing a swapfile of the appropriate size
SWAPSIZE=$(free | awk '/Mem/ {x=$2/1024/1024; printf "%.0fG", (x<2 ? 2*x : x<8 ? 1.5*x : x) }') 
sudo btrfs subvolume create /var/swap 
sudo chattr +C /var/swap 
sudo restorecon /var/swap 
sudo mkswap --file -L SWAPFILE --size $SWAPSIZE /var/swap/swapfile 
sudo bash -c 'echo /var/swap/swapfile none swap defaults 0 0 >>/etc/fstab' 
sudo swapon -av
  1. Add the appropriate SELinux rules to allow systemd to hibernate.

You have two options here - one is to try systemctl hibernate a few times and look at the output of audit2allow to figure out what is preventing it from working as per the steps in the forum post referenced above - but I’m guessing that the same base rules will apply to any silverblue installation, so you can also create a file systemd_hibernate.te with the following content:

module systemd_hibernate 1.0;

require {
type systemd_logind_t;
type init_var_lib_t;
type systemd_sleep_t;
type swapfile_t;
class dir { add_name search write };
class cap_userns sys_ptrace;
}

#============= systemd_logind_t ==============
allow systemd_logind_t self:cap_userns sys_ptrace;
allow systemd_logind_t swapfile_t:dir search;

#============= systemd_sleep_t ==============
allow systemd_sleep_t init_var_lib_t:dir write;
allow systemd_sleep_t init_var_lib_t:dir add_name;
allow systemd_sleep_t swapfile_t:dir search;

Then check, build and install the module:

sudo checkmodule -M -m -o systemd_hibernate.mod systemd_hibernate.te 
sudo semodule_package -o systemd_hibernate.pp -m systemd_hibernate.mod 
sudo semodule -i systemd_hibernate.pp

After a quick reboot, you can now test out whether systemctl hibernate works - if the light on the power button goes off, and you can boot back up to the state that you were previously in, it has! If it doesn’t work, you’ll need to do some troubleshooting, first thing is to run audit2allow -b and see if any rules have prevented the system from hibernating. The output will look similar to the above file, and you can generate further rules with sudo audit2allow -b -M systemctl_hibernate2 and then examine the systemctl_hibernate2.te file to understand what further rules need adding to the file above. I would recommend trying to keep all the related rules in a single file/module - it can get confusing otherwise. If this reveals nothing, you will be digging through kernel/boot logs (journalctl -b) and looking for anything related to systemd-sleep, hibernate, suspend-then-hibernate, etc.

  1. Set up lid-close and sleep settings

For me, the optimal behaviour was to suspend then hibernate after a period of time - I want my laptop to come back on straight away if I have only been away a few minutes, but then to hibernate and preserve battery after say 30 minutes. Unfortunately, Gnome hijacks some of these settings, so it’s not as easy as just setting up the systemd config. Here’s what worked for me.

First of all, tell Gnome to go idle after 5 minutes, keep its hands off the sleep settings, and set a 15 minute timeout on battery and a 30 minute timeout when charging:

gsettings set org.gnome.desktop.session idle-delay 300 gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-timeout 900 gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing' gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 1800 gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'

Then we need to create some config files to tell systemd how to sleep and hibernate. You can see the default files in /usr/lib/systemd/ if you want to see all the options available - use man logind.conf and man systemd-sleep.conf to see the details of the various options. First of all, create /etc/systemd/logind.conf as follows:

[Login] 
LidSwitchIgnoreInhibited=yes 
HoldoffTimeoutSec=20s 
IdleAction=suspend-then-hibernate 
IdleActionSec=30s

Then create /etc/systemd/sleep.conf:

[Sleep] HibernateDelaySec=300s

You can obviously adjust the timings above to your liking, to tinker with the gaps between screen-off, sleep, suspend and hibernate.

Hope this helps someone!