28 Feb 2017

Setting up suspend to hibernate in Fedora

Important Note: When you upgrade to the current version Fedora 26 (which right now - July 2017 - ships with systemd 233), you might run into a regression which prevents any units depending on the suspend one not to be executed. The same applies to any scripts and programs in /usr/lib/systemd/system-sleep/ which are supposed to be executed before and after one of the power save states is activates. As a consequence, suspend-to-hibernate is not working on Fedora as long as this issue is not fixed in upstream systemd and rolled out to Fedora :-(

Computers usually support two major energy saving modes: Suspend and Hibernate. Suspend basically shuts down all hardware components except your RAM (because it will preserve your current state there). Hibernate instead will write the current RAM contents to disk and then turn off everything. Both modes have their pro’s and con’s: Suspend is much faster, i.e. when you close your laptop lid you usually want the session to be back as soon as you open the lid again. So during the day you probably want to use this mode mostly. Hibernate on the other side has a greater potential for saving energy, as the system is basically shut down completely and restored to exactly this point later when you turn it on again. Hence, this mode is ideal when you seldom use your device (e.g. when you have to prepare for some events in your professional life and hence don’t have too much free time to spend with your private hardware ;-) ). Besides the energy and time components, there is another point that kicks in when using full disk encryption: Using suspend is less secure, because sensitive data is stored unencrypted in the still powered on RAM. Especially when you often take your laptop with you or in case of a laptop you use for your company, you surely don’t want someone else to gain access to your data. The good news is: You actually don’t have to decide for one or the other ;-) While it is not available out of the box, you can easily enable a mode where your laptop goes into suspend mode when you close your lid and later - say after one or two hours of not being used - it goes to hibernate, increasing energy saving and protecting your data in case you use full disk encryption. So, here is how to enable this “suspend-to-hibernate” mode in Fedora:

Enable Hibernate

You read right. If you have not already done so, you first have to enable Hibernate on your freshly installed Fedora box. This is due to the installer does not add the required parameters to the kernel boot arguments. Fortunately, this is a well documented task to do.

Enable Suspend-to-Hibernate

This as well is quite easy, even though not documented for the case of Fedora. However, our friends over at Arch Linux have summed up everything nicely in their Wiki and Forums: First, you need to create a new file /etc/systemd/system/suspend-to-hibernate.service with the following contents:

[Unit]
Description=Delayed hibernation trigger
Documentation=https://bbs.archlinux.org/viewtopic.php?pid=1420279#p1420279
Documentation=https://wiki.archlinux.org/index.php/Power_management
Before=suspend.target
Conflicts=hibernate.target hybrid-suspend.target
StopWhenUnneeded=true

[Service]
Type=oneshot
RemainAfterExit=yes
Environment="WAKEALARM=/sys/class/rtc/rtc0/wakealarm"

# Important: Here you can set the delay after when we go to hibernate:
Environment="SLEEPLENGTH=+2hour"

ExecStart=-/usr/bin/sh -c 'echo -n "alarm set for "; date +%%s -d$SLEEPLENGTH | tee $WAKEALARM'
ExecStop=-/usr/bin/sh -c '\
  alarm=$(cat $WAKEALARM); \
  now=$(date +%%s); \
  if \[ -z "$alarm" \] || \[ "$now" -ge "$alarm" \]; then \
     echo "hibernate triggered"; \
     systemctl hibernate; \
  else \
     echo "normal wakeup"; \
  fi; \
  echo 0 > $WAKEALARM; \
'

[Install]
WantedBy=sleep.target

Basically, you can ignore most stuff if you don’t care. The most interesting part is the line after the Important: comment: It determines the time the computer shall remain in suspend before going to hibernate. The default two hours are a decent choice for most use cases. In theory, you could try to enable this new service by running

sudo systemctl enable suspend-to-hibernate

However, some updates in systemd obviously changed something that prevents the service from working flawlessly with the suspend service delivered with systemd itself. The good thing is, that this is easily fixed. Run the following

sudo cp /usr/lib/systemd/system/suspend.target /etc/systemd/system/

To create a local copy of the suspend target definition and edit it to include the part after the line marked with Important: below (keep everything else as it is to be as near at the default definition as possible):

#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Suspend
Documentation=man:systemd.special(7)
DefaultDependencies=no
Requires=systemd-suspend.service
After=systemd-suspend.service
StopWhenUnneeded=yes

# Important: Add a dependency to our suspend-to-hibernate service:
Requires=suspend-to-hibernate.service

That’s it. If you’ve not already done so enable the suspend-to-hibernate service as shown above using systemctl enable.

Thank You For Reading
Martin Hoeher

I am a software/firmware developer, working in Dresden, Germany. In my free time, I spent some time on developing apps, presenting some interesting findings here in my blog.

Comments

Frank Jacobberger

Excellent documentation except for “To create a local copy of the suspend target definition and edit it to include the line marked bold below (keep everything else as it is to be as near at the default definition as possible):

Under [Unit] there is nothing marked bold here:

[Unit] Description=Suspend Documentation=man:systemd.special(7) DefaultDependencies=no BindsTo=systemd-suspend.service After=systemd-suspend.service Requires=suspend-to-hibernate.service

Or are you saying replace every line item with what’s highlighted in red?

Thanks,

Martin Hoeher

You are right, sorry. This is an artifact introduced due to I migrated the blog from Wordpress to Jekyll. Currently, in the sample, there is a line which starts and ends with two *. Basically, these were meant to mark the line as bold (which it obviously did not). I’ll correct the sample ASAP, so: Thanks for the heads up!

Martin Hoeher

I’ve already updated the article, check out the code sample, it should be clearer now ;-)

Frank Jacobberger

No change at all.

This is what appears now:

[Unit] Description=Suspend Documentation=man:systemd.special(7) DefaultDependencies=no BindsTo=systemd-suspend.service After=systemd-suspend.service Requires=suspend-to-hibernate.service

All the best, Frank

Martin Hoeher

You are right, there were actually two instances of the issue. I only fixed the first one.

I updated the complete article now, so it should be complete and working.

Your comment has been filed

We'll review it and it will appear here as soon as we're done.

Sorry, something went wrong...

Your comment could not be posted.

Regarding your personal information...
  • The name you enter will be shown next to your comment. You may enter your real name or whatever you like.
  • Your e-mail will be used to show an image representing you next to your comment. We do not store nor show your address somewhere. Instead, an ID will be calculated from it and stored. This ID is then used to retrieve an image from Gravatar.