How to set up Hibernate on Ubuntu 20.04
Most modern devices are running large SSD's, so there's no reason not to set up hibernate on your linux system. It's really convenient to be able to snapshot your working state if you need to power off your machine or reboot into a different OS.
This is a comprehensive guide that shows how to set up Hibernation on Ubuntu 20.04. It is based heavily on this stackoverflow answer plus a whole plethora of reddit posts and linux man pages.
Some jargon to know before we dive in:
- Swap: Specially allocated disk space that extends RAM if it fills up. RAM can also be written to swap when suspending.
- Sleep: Also known as "Suspend to RAM". Writes all data to RAM, computer goes to sleep quickly and wakes up instantly. State lost on power loss, and this requires constant energy to DRAM.
- Hibernate: Also known as "Suspend to Disk". Writes all RAM contents on disk, and computer can turn off completely. Requires enough swap space (either file or partition) to store the entire contents of ram. Does not require any energy usage while off, and can survive power disruptions.
Prerequisites
Kernel Support
Check your available kernel sleep states with cat /sys/power/state
. You'll see something like freeze mem disk
. The disk
part is crucial: this is what we need to suspend to disk. Check cat /sys/power/disk
and make sure it's not disabled. If it is, check your Secure Boot settings.
Explanation of other options:standby
is for Power-on-Suspend andfreeze
is for suspend-to-idle
For more details, check this stackoverflow answer .
Swap Space
First, make sure you have enough disk space to store the contents of your RAM.
- Find out how much ram you have:
free -mh | grep "Mem"
- the first column will be your total - Check your disk space:
df -h
- look for the line that is mounted on/
- Check for existing swap:
cat /etc/fstab | grep swap
Your swap space can be either a disk file or a partition. I recommend going with the swap partition instead of a file for two reasons:
- You don't have to worry about file offsets and swap file fragmentation
- It's easier to do backups of your root partition, since you don't have to remember to set up excludes on your
/swapfile
. It really sucks to back up 32gb of useless data.
If your results from step 3 above showed a swap file, check the size of it with du -sh /swapfile
. If it's less than your total RAM (from step 1) you'll need a bigger file.
If your /etc/fstab
shows something like UUID=2ae674d7-6b75-4680-93c5-6d11c7bfb9b3 none swap sw 0 0
, this means you have a swap partition. You can get more details about it with: lsblk | grep SWAP
. The 4th column will show the size in GB. Again, if it's smaller than your total RAM you're going to have to make a bigger partition. The details are out of scope for this guide - you can check the ubuntu wiki for more information. You will probably want to use gdisk or gparted to create a large enough swap partition.
Configure swap partition
Now that you've set up your swap partition, let's grab the UUID of it.
blkid | grep swap
You'll see something like:
/dev/sdb6: UUID="2ae674d7-6b75-4680-93c5-6d11c7bfb9b3" TYPE="swsuspend" PARTLABEL="swap" PARTUUID="ab9cf927-6daa-4b7a-a548-2c0a7de0dcec"
Our UUID is "2ae674d7-6b75-4680-93c5-6d11c7bfb9b3". Hang on to this, we're going to need this UUID a ton in the next few steps.
Optional: Add new swap to /etc/fstab.
If you had to create a swap partition, you'll need to add it to the /etc/fstab
.
Run sudo vim /etc/fstab
and add the line UUID=2ae674d7-6b75-4680-93c5-6d11c7bfb9b3 none swap sw 0 0
, where the UUID string is replaced with the value you got earlier in the blkid
command.
Turn on your swap and verify that it works with sudo swapon --all --verbose
and cat /proc/swaps
. If you run free -mh
now, you should see a Swap
line.
Test #1
You can test this setup with sudo systemctl hibernate
. Your computer should turn off, but when you boot it back up it will not resume. This is expected, since we haven't configured the resume part yet.
Install the tools
We'll need the proper tools to enable hibernate:
sudo apt install pm-utils hibernate
Set up "resume" in grub and initramfs
Now that the swap space is created, we can inform the kernel where to resume from.
First, we need to update the grub config. Run sudo vim /etc/default/grub
and find the line GRUB_CMDLINE_LINUX_DEFAULT
. This is the list of parameters passed to the kernel on boot. it will probably be set to quiet splash
. You'll want to add the string resume=UUID=2ae674d7-6b75-4680-93c5-6d11c7bfb9b32
, replacing my UUID with yours.
Optional: remove the "quiet splash". I like to see the boot logs since it makes it easier to debug things when they inevitably break. Plus, it scares my friends so that's fun too.
Update grub to pick up these changes: sudo update-grub
We're also going to need to regenerate the initramfs and provide it the resume parameter:
# write the resume UUID (make sure to replace this with your UUID)
echo RESUME=UUID=2ae674d7-6b75-4680-93c5-6d11c7bfb9b2 | sudo tee /etc/initramfs-tools/conf.d/resume
# regenerate initramfs
sudo update-initramfs -c -k all
Test #2
Let's try the sudo systemctl hibernate
command again. If all went well, this time your system should completely power off, and then resume successfully to your desktop. If something didn't work, remove the quiet splash
from /etc/default/grub
, run sudo update-grub
and read the logs to see what went wrong.
Again, check this stackoverflow answer for debugging tips as well as the ubuntu wiki.
If all went well, congratulations! You now have hibernate working on your ubuntu 20.04 installation. If you'd like to set up passwordless hibernation, read on.
Enable Hibernation without password
We've achieved our primary goal of setting up hibernation on Ubuntu 20.04. However, it's even more convenient to have a simple shortcut that can auto lock and hibernate our system.
First, let's allow our user to run the systemctl hibernate
command without having to provide sudo
.
sudo update-alternatives --config editor # pick your editor
sudo visudo /etc/sudoers
Add the following lines to this file, replacing yourname
with your username:
yourname ALL= NOPASSWD: /usr/bin/systemctl hibernate
yourname ALL= NOPASSWD: /usr/bin/systemctl suspend
Test #3
You should now be able to run systemctl hibernate
. If your system hibernates, you're done!
Polkit
On my Ubuntu 20.04 system, the visudo
changes above were not enough to get a non-interactive hibernate working. I no longer needed to provide the sudo password, but I still saw an authentication dialog:
This dialog comes from polkit, which is an application level policy handler that allows or blocks access to privileged actions. In this case, we are attempting to use the privileged action of triggering a systemd
unit.
If we expand the Details
tab, we can see what exact action is being triggered:
The "Action" is org.freedesktop.systemd1.manage-units
. We can pick an "Identity" and grant it access to this action. For this example, we'll use the unix group users
, so our Identity will be unix-group:users
Make sure that your user is part of that group by running sudo usermod -aG users "$USER"
.
Then, run sudo su
and go to the /var/lib/polkit-1/localauthority/50-local.d
directory. We'll add a file called 51-systemd-manage-units.pkla
with the following contents:
[Allow users to manage systemd units]
Identity=unix-group:users
Action=org.freedesktop.systemd1.manage-units
ResultAny=yes
ResultInactive=yes
ResultActive=yes
Note that the Identity
can be any group or user you pick ( unix-group:myspecialgroup
or unix-user:potato
).
If you're curious about the other keys, you can check the "Authorization Entry" header on [this](https://www.freedesktop.org/software/polkit/docs/0.105/pklocalauthority.8.html) page.
This change is picked up immediately - no logout/reboot required.
Test #4
You can test this command by running systemctl hibernate
. Your system should immediately go into the hibernate mode we saw previously, without a password prompt.
Password Protection
After resuming, you'll notice that you're taken to the exact screen you had before you ran the hibernate command. This is of course expected, but you probably want to set up some password protection for best practices. There are two options that come to mind:
- Set up an encrypted swap partition
- Lock the screen before triggering hibernate.
I'm setting this up on my home desktop, so I don't have an encrypted root partition. As such, encrypting the swap is kinda pointless.
Instead, we can set up a command that will lock the desktop and then trigger hibernate.
xsecurelock
You have many different options. Ultimately, all you need is your favorite lock screen program plus a way to trigger a shell script easily. In this example, we'll use xsecurelock.
# Install prerequisites
sudo apt installapache2-utils autotools-dev binutils gcc libc6-dev libpam-dev libx11-dev libxcomposite-dev libxext-dev libxfixes-dev libxft-dev libxmuu-dev libxrandr-dev libxss-dev make pamtester
# Clone the repo
git clone https://github.com/google/xsecurelock.git
# Generate config.
cd xsecurelock
sh autogen.sh
# Configure with PAM from /etc/pam.d/ - we'll use "common-auth"
./configure --with-pam-service-name=common-auth
# make and install
make
sudo make install
Test this by running xsecurelock
from your terminal. You should see a simple black screen, and if you start typing you'll see the password field. More options available at https://github.com/google/xsecurelock#options. You may want to set XSECURELOCK_DATETIME_FORMAT
, XSECURELOCK_PASSWORD_PROMPT
or XSECURELOCK_SHOW_DATETIME
Beep Indicator
Personally, I like to get additional signals in the form of colors or sound when my various devices are doing complex actions. I looked into it, but there doesn't seem to be a good way to hook into the hibernate process to signal progress. There's a beep utility in linux, but it relies on the blacklisted `pcspkr` module. If you have suggestions, please email me :)
Putting it all together
At this point we have working hibernate, proper authentication to hibernate non-interactively, and a screen lock. We can create a simple wrapper shell script at ~/bin/hibernate
with the following contents:
#!/usr/bin/env bash
xsecurelock && sleep 1 && systemctl hibernate
How your trigger this is up to you. You can run it manually in a terminal, trigger it with rofi, or if using i3wm add a keybind: bindsym $MOD+Shift+h exec "xsecurelock && sleep 1 && systemctl hibernate"