Hibernation on Fedora Workstation fails for a reason most guides never mention: Secure Boot. With Secure Boot on, the kernel runs in lockdown mode and refuses to hibernate at all, no matter how much swap you give it. Get past that, and three more things stop you on Fedora specifically. The default swap is zram, which lives in RAM. A new Btrfs swap subvolume comes up with no SELinux label, so logind is denied access to it. And the initramfs has no resume support, so the machine writes its memory to disk and then boots fresh instead of restoring it.
We hit every one of those on a fresh Fedora 45 Workstation install with Secure Boot enabled, fixed them in order, and confirmed a real resume each time by leaving a process running and checking it was still there afterwards. This guide follows the same order. It also covers suspend-then-hibernate, what GNOME does and does not offer, and how to put Secure Boot validation back.
Tested October 2026 on Fedora 45 Workstation (UEFI, Secure Boot keys enrolled), SELinux enforcing, Btrfs root.
What stops hibernation on a default Fedora install
Start by checking where you stand. These four commands answer most of it:
mokutil --sb-state; cat /sys/kernel/security/lockdown; cat /sys/power/state; cat /sys/power/disk
On our machine Secure Boot was on, lockdown was in integrity mode, and disk was missing from the list of sleep states:
SecureBoot enabled
none [integrity] confidentiality
freeze mem
[disabled]
That missing disk entry is the whole story. Trying anyway gets a generic error from systemd, and the kernel log names the real cause:
systemctl hibernate 2>&1; journalctl -b -k --no-pager | grep -i -E "lockdown|hibernat" | tail -3
The kernel refuses because lockdown restricts hibernation:
Call to Hibernate failed: Sleep verb 'hibernate' is not configured or configuration is not supported by kernel
Oct 06 00:04:29 ws-lab01 kernel: Lockdown: cat: hibernation is restricted; see man kernel_lockdown.7
Oct 06 00:04:29 ws-lab01 kernel: Lockdown: cat: hibernation is restricted; see man kernel_lockdown.7
Oct 06 00:04:29 ws-lab01 kernel: Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
Lockdown blocks hibernation because the image is written to disk unsigned. Anyone who can write to that disk could change the image and have it loaded as trusted kernel memory on the next boot, which would bypass Secure Boot. If /sys/power/disk shows [disabled] on your machine, no amount of swap configuration will help until Step 3.
The other blockers are quieter. The same machine started out like this:
grep PRETTY /etc/os-release; uname -r; free -h | head -2; swapon --show; findmnt -no FSTYPE,SOURCE /
The only swap is zram, a compressed swap device in RAM. Its contents are gone at power-off, so it cannot hold a hibernation image:
PRETTY_NAME="Fedora Linux 45 (Workstation Edition Prerelease)"
7.2.7-300.fc45.x86_64
total used free shared buff/cache available
Mem: 3.8Gi 1.6Gi 854Mi 19Mi 1.7Gi 2.2Gi
NAME TYPE SIZE USED PRIO
/dev/zram0 partition 3.8G 0B 100
btrfs /dev/sda3[/root]
You need disk-backed swap at least as large as the memory in use. On a Btrfs root, that means a swapfile in its own subvolume.
Step 1: Create a swapfile in a dedicated Btrfs subvolume
Btrfs refuses to snapshot a subvolume that holds an active swapfile, so the swapfile gets its own subvolume and root stays snapshottable for tools like Snapper and grub-btrfs. btrfs filesystem mkswapfile creates the file with copy-on-write disabled. --uuid clear writes the swap header without a UUID; nothing in this setup refers to the swap by UUID, so the default random one works just as well. We sized it to match RAM (3.8 GiB here), rounded up to 4 GiB:
sudo btrfs subvolume create /var/swap; sudo btrfs filesystem mkswapfile --size 4g --uuid clear /var/swap/swapfile; sudo ls -lZ /var/swap/swapfile; sudo lsattr /var/swap/swapfile
Two things in the output matter. The C attribute confirms no copy-on-write. The SELinux label is unlabeled_t, and that breaks hibernation later:
Create subvolume '/var/swap'
create swapfile /var/swap/swapfile size 4.00GiB (4294967296) page size 4096
-rw-------. 1 root root unconfined_u:object_r:unlabeled_t:s0 4294967296 Oct 6 00:05 /var/swap/swapfile
---------------C------ /var/swap/swapfile
Enable the swapfile and make it permanent. Priority 0 keeps zram first for everyday swapping, so the swapfile is only used when zram is full or when you hibernate:
sudo swapon /var/swap/swapfile; echo "/var/swap/swapfile none swap defaults,nofail,pri=0 0 0" | sudo tee -a /etc/fstab; swapon --show
Both swap devices are active:
/var/swap/swapfile none swap defaults,nofail,pri=0 0 0
NAME TYPE SIZE USED PRIO
/dev/zram0 partition 3.8G 0B 100
/var/swap/swapfile file 4G 0B -1
The priority shows as -1 until the next boot, when the fstab entry applies it.
Step 2: Label the swapfile for SELinux
Do not skip this step. Fedora’s policy has no file-context rule for swap files, so the expected label for /var/swap is plain var_t, and the new subvolume came up with no label at all:
matchpathcon /var/swap /var/swap/swapfile; sudo semanage fcontext -l | grep -E "swapfile_t" | head -5
No rule mentions swapfile_t:
/var/swap system_u:object_r:var_t:s0
/var/swap/swapfile system_u:object_r:var_t:s0
The failure mode is a misleading error. Once Secure Boot was out of the way, sudo systemctl hibernate answered with a bare Access denied, even as root. The audit log shows why: logind was not allowed to search the unlabeled subvolume to find the swapfile:
sudo journalctl -b --no-pager -t audit | grep "systemd-logind" | grep denied | tail -1
The denial names systemd-logind, the swap directory and unlabeled_t:
Oct 06 00:12:16 ws-lab01 audit[702]: AVC avc: denied { search } for pid=702 comm="systemd-logind" name="swap" dev="sda3" ino=256 scontext=system_u:system_r:systemd_logind_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=0
Add a permanent rule for the swap directory and relabel it:
sudo semanage fcontext -a -t swapfile_t "/var/swap(/.*)?"; sudo restorecon -RFv /var/swap; sudo ls -dZ /var/swap /var/swap/swapfile
Both the directory and the file now carry swapfile_t:
Relabeled /var/swap from system_u:object_r:unlabeled_t:s0 to system_u:object_r:swapfile_t:s0
Relabeled /var/swap/swapfile from unconfined_u:object_r:unlabeled_t:s0 to system_u:object_r:swapfile_t:s0
system_u:object_r:swapfile_t:s0 /var/swap
system_u:object_r:swapfile_t:s0 /var/swap/swapfile
The rule survives relabels and policy updates, because it lives in the local SELinux policy rather than on the file alone.
Step 3: Lift the Secure Boot lockdown
With Secure Boot on, nothing else in this guide works. You have two choices, and both lower the protection Secure Boot gives you:
- Turn Secure Boot off in the firmware setup. This affects every operating system on the machine.
- Keep firmware Secure Boot on, and tell shim to stop validating what it loads with
mokutil --disable-validation. Fedora’s kernel then treats Secure Boot as off and drops lockdown.
We used the second option, because it is reversible from Fedora and needs no trip into vendor firmware menus. Be clear about the cost: with validation off, the boot chain will load an unsigned or tampered kernel or module. On a laptop that leaves your hands, that is a real downgrade. Only do this if you need hibernation more than you need a verified boot chain.
Queue the change. mokutil asks for a one-time password of 8 to 16 characters, which you’ll type again at the next boot:
sudo mokutil --disable-validation
The prompts look like this:
[sudo] password for mercy:
password length: 8~16
input password:
input password again:
Reboot. Before Fedora starts, shim shows a blue MOK management screen for a few seconds. Press a key to stop the countdown, then choose Change Secure Boot state:

MokManager doesn’t ask for the whole password. It asks for three random characters of it, one at a time. Type the requested character and press Enter after each one:

Answer Yes to Disable Secure Boot, then pick Reboot on the next menu:

Back in Fedora, check the same four values again:
mokutil --sb-state; cat /sys/kernel/security/lockdown; cat /sys/power/state; cat /sys/power/disk; swapon --show
Firmware Secure Boot is still enabled, shim validation is off, lockdown is none, and disk is now a valid sleep state:
SecureBoot enabled
SecureBoot validation is disabled in shim
[none] integrity confidentiality
freeze mem disk
[shutdown] reboot suspend test_resume
NAME TYPE SIZE USED PRIO
/var/swap/swapfile file 4G 0B 0
/dev/zram0 partition 3.8G 0B 100
Step 4: Add resume support to the initramfs
This is the step that catches people after everything else looks right. Our first successful hibernate wrote the image and powered off cleanly. On the next boot, the machine started fresh. The process we left running was gone and the boot ID had changed:
pgrep -a sleep | grep 86400; cat /proc/sys/kernel/random/boot_id; uptime -p; sudo journalctl -b --no-pager | grep -iE "PM: (hibernation|image|resume)|Restoring|hibernation exit|Hibernation" | tail -8
No marker process, a new boot ID, and an uptime of zero:
6e3aacbc-3e24-4829-a45d-301999f733b1
up 0 minute
The last journal line, after a run of nosave memory lines, shows that systemd did find the image:
Oct 06 00:16:42 ws-lab01 systemd-hibernate-resume[491]: Reported hibernation image: ID=fedora VERSION_ID=45 kernel=7.2.7-300.fc45.x86_64 UUID=f46983c5-ff8c-462e-b078-a4fcc2b02d5b offset=4596992
It found it too late. That line comes from the real root filesystem, after the initramfs had already mounted it, and the image is useless by then. The initramfs has no resume module. In host-only mode, dracut adds it only when it sees a resume= kernel argument or an active resume device, and neither exists when the hibernation location is passed through EFI:
sudo lsinitrd -m | grep -E "^(resume|systemd)$"; sudo lsinitrd | grep -c hibernate-resume
There is no resume module and no hibernate-resume code inside the image:
systemd
0
Add the module permanently with a dracut drop-in, then rebuild the initramfs for the running kernel. Fedora 45 ships dracut 111. Upstream dracut 112 includes resume by default when the kernel supports it, so once Fedora picks it up the drop-in becomes redundant, but it does no harm:
sudo sh -c "echo add_dracutmodules+=\\\" resume \\\" > /etc/dracut.conf.d/resume.conf"; cat /etc/dracut.conf.d/resume.conf; sudo dracut -f; sudo lsinitrd -m | grep -E "^resume$"; sudo lsinitrd | grep -c hibernate-resume
The file holds one line, and the rebuilt initramfs now carries the module:
add_dracutmodules+=" resume "
resume
3
Future kernel updates pick up the drop-in automatically.
You do not need a resume= or resume_offset= kernel argument on a UEFI machine. When systemd hibernates, it stores the swapfile location in an EFI variable called HibernateLocation, and the resume code in the initramfs reads it on the next boot. Our kernel command line had neither argument, and resume worked.
Step 5: Hibernate and prove it resumed
A desktop that comes back looking the same proves nothing, because GNOME restores its windows on a fresh login too. Leave a marker process running and note the boot ID first:
nohup sleep 86400 >/dev/null 2>&1 & echo "marker pid $!"; cat /proc/sys/kernel/random/boot_id; date +%T
Note the PID and the boot ID:
marker pid 10132
6e3aacbc-3e24-4829-a45d-301999f733b1
00:18:18
Then hibernate. From a terminal in your desktop session, systemctl hibernate is enough. From SSH or any session that is not the active one on the seat, polkit asks for admin rights, so use sudo systemctl hibernate. The machine powered off two seconds later. After powering it back on, run the check again:
pgrep -a sleep | grep 86400; cat /proc/sys/kernel/random/boot_id; date +%T; uptime -p
The marker is alive with the same PID, and the boot ID did not change. That is a real resume:
10132 sleep 86400
6e3aacbc-3e24-4829-a45d-301999f733b1
00:18:51
up 2 minutes
On our 4 GB VM, 33 seconds passed between the marker check before hibernating and the one after resume, including the power-on. For sizing, the kernel log from the later suspend-then-hibernate run in Step 6 is the useful one. Its in-memory snapshot needed 357,808 pages, about 1.4 GiB, so a swapfile the size of RAM leaves plenty of room:
journalctl -b --no-pager -k | grep -E "PM: (hibernation|Image|Read|Wrote|Using|Starting|Need|Saving|Restoring|Image loading)" | grep -vE "nosave|bitmaps" | grep -iE "wrote|read|image|saving|restor|pages" | head -10
The first line is the harmless probe at boot, when there was no image to restore. The rest show the kernel allocating the snapshot from the free pages (the timestamps are from that later run):
Oct 06 00:35:58 ws-lab01 kernel: PM: Image not found (code -16)
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Preallocating image memory
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Allocated 357808 pages for snapshot
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Normal pages needed: 356492 + 1024, available pages: 685929
The session comes back locked, and you need your password to get in. Keep it that way. The hibernation image holds everything that was in memory, including unlocked keyrings and browser sessions.

Step 6: Use suspend-then-hibernate on a laptop
Plain hibernation is slow to enter and slow to leave. On a laptop, suspend-then-hibernate is the better default: the machine suspends as usual, wakes itself after a set time, and hibernates so the battery does not drain overnight. Set the delay in a drop-in. We used 60 seconds to test; one or two hours suits a real laptop:
sudo mkdir -p /etc/systemd/sleep.conf.d; sudo sh -c "printf \"[Sleep]\nHibernateDelaySec=60s\n\" > /etc/systemd/sleep.conf.d/hibernate-delay.conf"; cat /etc/systemd/sleep.conf.d/hibernate-delay.conf; busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSuspendThenHibernate
logind reports challenge here only because we asked from SSH. In the desktop session it reads yes:
[Sleep]
HibernateDelaySec=60s
s "challenge"
We ran sudo systemctl suspend-then-hibernate with a marker process running, then pulled the sequence from the journal after the resume:
pgrep -a sleep | grep 86403; cat /proc/sys/kernel/random/boot_id; journalctl -b --no-pager --since 00:25:50 | grep -E "systemd-sleep|PM: suspend (entry|exit)|PM: hibernation" | grep -v -E "audit|nosave" | head -10
The marker survived, and the journal shows each stage:
2255 sleep 86403
7fb8ff4f-0a34-489b-b33d-5910c6966724
Oct 06 00:25:53 ws-lab01 systemd-sleep[2410]: Successfully froze unit 'user.slice'.
Oct 06 00:25:53 ws-lab01 systemd-sleep[2410]: Performing sleep operation 'suspend'...
Oct 06 00:25:53 ws-lab01 kernel: PM: suspend entry (s2idle)
Oct 06 00:35:58 ws-lab01 systemd-sleep[2410]: System returned from sleep operation 'suspend-then-hibernate'.
Oct 06 00:35:58 ws-lab01 kernel: PM: suspend exit
Oct 06 00:35:58 ws-lab01 systemd-sleep[2410]: Performing sleep operation 'hibernate'...
Oct 06 00:35:58 ws-lab01 kernel: PM: hibernation: hibernation entry
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Basic memory bitmaps created
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Preallocating image memory
Oct 06 00:36:49 ws-lab01 kernel: PM: hibernation: Allocated 357808 pages for snapshot
Read the timestamps carefully. The VM entered suspend at 00:25:53, but the wake alarm never fired inside QEMU, so it stayed suspended until we pressed a key ten minutes later. systemd then saw the delay had passed and hibernated, and the marker process survived the resume. On real laptop hardware the RTC alarm does the waking. We could not test that part in a VM, so check it on your own machine with a short delay before relying on it.
To make closing the lid do the same, add HandleLidSwitch=suspend-then-hibernate under [Login] in a file in /etc/systemd/logind.conf.d/, then run sudo systemctl reload systemd-logind or reboot. HandleLidSwitchExternalPower follows the same value unless you set it separately. A VM has no lid, so that line is untested here.
What GNOME offers, and what it does not
GNOME has no Hibernate button. Even with the kernel ready and logind reporting hibernation as available, the power menu shows only Suspend, Restart and Power Off:

The power button is where GNOME exposes it. Once logind reports hibernation as available, Settings > Power > Power Button Behavior lists Hibernate as a choice. GNOME hides that whole row when the machine is a virtual machine, so our lab VM never showed it. We set the same value with gsettings, which is also the route over SSH:
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus; gsettings range org.gnome.settings-daemon.plugins.power power-button-action; gsettings set org.gnome.settings-daemon.plugins.power power-button-action hibernate; gsettings get org.gnome.settings-daemon.plugins.power power-button-action
The setting took:
enum
'nothing'
'suspend'
'hibernate'
'interactive'
'hibernate'
The export is only needed when you run it over SSH; in a desktop terminal, the plain gsettings set line is enough. Our VM could not prove the power button. The settings daemon logged that virtual machines always shut down on a power-button press:
sudo journalctl -b -1 --no-pager | grep -m1 "Virtual machines only honor"
The warning is explicit:
Oct 06 00:24:44 ws-lab01 gsd-media-keys[1265]: Virtual machines only honor the 'nothing' power-button-action, and will shutdown otherwise
It did exactly that, twice. On laptop and desktop hardware the setting applies; test it once with a marker process before you trust it. For everything else, systemctl hibernate from a terminal or a keyboard shortcut works in any GNOME session.
Put Secure Boot validation back
If hibernation turns out not to be worth the trade, reverse Step 3. The swapfile and dracut changes can stay; they do no harm with lockdown back on.
sudo mokutil --enable-validation
The prompts are the same as before:
[sudo] password for mercy:
password length: 8~16
input password:
input password again:
Reboot, choose Change Secure Boot state in MokManager, type the requested characters and answer Yes to Enable Secure Boot. We did this on the same machine afterwards:
mokutil --sb-state; cat /sys/kernel/security/lockdown; cat /sys/power/disk; systemctl hibernate 2>&1
Validation is back, lockdown returned to integrity, and hibernation is blocked again:
SecureBoot enabled
none [integrity] confidentiality
[disabled]
Call to Hibernate failed: Sleep verb 'hibernate' is not configured or configuration is not supported by kernel
Hibernation errors and what each one means
Most of these came up during testing; the swap-space one is what logind returns if you skip Step 1. They are listed by the message you see.
Sleep verb ‘hibernate’ is not configured or configuration is not supported by kernel
The kernel offers no disk state. On Fedora the usual cause is Secure Boot lockdown; confirm it with cat /sys/kernel/security/lockdown and the hibernation is restricted line in journalctl -k. With lockdown at [none], this message points at the kernel itself, for example one built without hibernation support.
Not enough suitable swap space for hibernation available on compatible block devices and file systems
logind found no disk swap that can hold the image. zram does not count, and Fedora’s only swap out of the box is zram. Create the swapfile from Step 1 and check that swapon --show lists it.
Call to Hibernate failed: Access denied
A plain Access denied, even with sudo, came from SELinux. logind could not search the unlabeled /var/swap subvolume. Search the audit log for systemd-logind denials and apply the swapfile_t rule from Step 2.
Access denied as the requested operation requires interactive authentication
This one is polkit, and it’s working as designed. A session that isn’t the active one on the seat, such as SSH, needs admin rights to hibernate. Use sudo systemctl hibernate there. This is what the unprivileged call returns:
systemctl hibernate 2>&1; echo "exit=$?"
The message names the missing authentication:
Call to Hibernate failed: Access denied as the requested operation requires interactive authentication. However, interactive authentication has not been enabled by the calling program.
exit=1
The machine hibernates, then boots fresh
The image was written but never restored. Check sudo lsinitrd -m for resume; if it’s missing, add the dracut drop-in from Step 4 and rebuild. Also check that your disk is encrypted. Our lab disk wasn’t, which means the image of everything in memory sat on disk in the clear. Resume only resets the swap signature, so those pages stay in the swapfile until something overwrites them. On a laptop, install Fedora with Encrypt my data so the swapfile lives inside LUKS. We didn’t test resume with LUKS, so test it on your own machine with a marker process.
For the rest of the post-install work on this release, see our list of things to do after installing Fedora 45. If you dual boot with Windows, read how to dual boot Fedora and Windows 11 safely before you touch Secure Boot settings, because BitLocker reacts to boot chain changes. Our NVIDIA driver guide for Fedora covers the same MOK enrollment screens for signing kernel modules, and the Fedora 45 release overview lists the other changes in this cycle.