Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Fedora 35 hibernation with swapfile, only for hibernation and resume

Fedora35 hibernation

This guide helps to configure the hibernation on a default Fedora35 (also worked fine in previous Fedora34) installation by using a swap file. The Fedora35 installation comes with btrfs as default filesystem. Also, it comes with a zram swap device:

$ swapon
/dev/zram0 partition   8G   0B  100

This device reserves a physical memory area in which all the content will be compressed (at its input) and uncompressed (at its output). But, we cannot hibernate with this type of swap space as it is only in memory.

After feedback received from contributors of this guide, seems we can say that this guide will work in both, encrypted and unencrypted setups.

important note before you continue: Some of us are experimenting problems with nvidia proprietary drivers. See below comments for more information on the investigation.


Nowadays we have lots of RAM in our laptops. In my case, a guy with a laptop for programming, i only use swap space for hibernation. When i move from one location to another, i dont want to loose all my open stuff nor consuming battery while i am moving.

I want to preserve the zram swap device of the default configuration.


This are the steps we need to perform in our system:

  1. The hibernation is triggered by the user.
  2. The swap file is activated.
  3. The zram device is deactivated. If there are any memory pages present in zram, they will be moved to the activated (2) swap file.
  4. Hibernate the laptop.

And the following sequence for resuming:

  1. Power on the computer.
  2. Restore system state from the swap file at boot time.
  3. Activate the zram device.
  4. Deactivate the swap file. That could cause the zram device to start compressing data.

Seems there are efforts in order to make this much better, even with dynamic sized swap files, so only generating swap files for hibernation with the needed size on each moment, and more user friendly.

This guide will make use of a fixed size swap file, since probably for going further is better to just contribute in the mentioned systemd issue .


Default Fedora35 installations comes with btrfs as default filesystem. Such file system comes with the subvolume feature. Subvolumes are not partitions. They are just a logical separations of a filesystem at a file level. Some kind of operations like snapshots, will try to include the swap file. We need to prevent this by isolating the swap file into its own subvolume:

btrfs subvolume create /swap

The above will ensure the swap file will not be taken into account in other snaphots as they are not recursive in other subvolumes.

Next is to create our swapfile for hibernation. Ensure the specified size is enough for saving the contents of the RAM + the uncompressed contents of the zram swap space. As root:

touch /swap/swapfile
chattr +C /swap/swapfile  ## Needed to disable Copy On Write on the file.
fallocate --length 33GiB /swap/swapfile  ## Please, calculate your size as mentioned in above comments.
chmod 600 /swap/swapfile 
mkswap /swap/swapfile 

Now lets prepare the path of the resume operation. We need add to the initramfs the necessary modules for resuming the system. We need to create a new file at /etc/dracut.conf.d/resume.conf with the following content:

add_dracutmodules+=" resume "

After that we need to regenerate the initramfs:

dracut -f

Next, we have to add the resume and resume_offset into the GRUB_CMDLINE_LINUX, so that Grub can instruct the kernel coordinates where the swap file resides, in order to resume the system.

For gathering the resume param we need the partition UUID in which the swap file is stored:

$ findmnt -no UUID -T /swap/swapfile

Now lets gather the last needed data, we need the resume_offset. That is, the physical offset of our swap file in the file system. For doing that, we need to follow this guide.

Now we need to instruct GRUB to initialize the kernel with this coordinates. Edit the /etc/defaut/grub and grab there the parameters we just calculated:

GRUB_CMDLINE_LINUX="rd.luks.uuid=luks-4369a407-2be1-4f37-9764-ff848a0f2089 resume=UUID=dbb0f71f-8fe9-491e-bce7-4e0e3125ecb8 resume_offset=2459934 rhgb quiet"

Now lets re-configure the grub (UEFI setup assumed):

grub2-mkconfig -o /boot/grub2/grub.cfg

Note: We only want the swap file to be used when the hibernation takes place and in the resume stage. we are not going to configure fstab entries. To achieve that, one of the options is to use systemd. We are going to configure 2 systemd services. One for preparing the hibernation and the other one for resuming it:

For enabling the swap file and disabling the zram swap device before hibernation, lets create the file /etc/systemd/system/hibernate-preparation.service:

Description=Enable swap file and disable zram before hibernate

ExecStart=/bin/bash -c "/usr/sbin/swapon /swap/swapfile && /usr/sbin/swapoff /dev/zram0"


The order is important in the above service definition. First of all we enable the swap file, which should have enough space to store the contents of the "in use RAM", plus the contents of the uncompressed zram swap device. Secondly, we disable the zram swap device. At that moment, the kernel will start moving all the memory pages from the zram swap device to the swap file if needed. After all of that, the hibernation will take place only in the swap file. Last step for the above script to have effect is to install this service in systemd:

# systemctl enable hibernate-preparation.service
Created symlink /etc/systemd/system/systemd-hibernate.service.wants/hibernate-preparation.service → /etc/systemd/system/hibernate-preparation.service.

We need to disable the swap file when the system just resumed. Lets create the /etc/systemd/system/hibernate-resume.service file:

Description=Disable swap after resuming from hibernation

ExecStart=/usr/sbin/swapoff /swap/swapfile


Then enable it by:

# systemctl enable hibernate-resume.service

Created symlink /etc/systemd/system/ → /etc/systemd/system/hibernate-resume.service.

In order to make the suspend-then-hibernate sequence to work, please take a look at this great comment and follow the instructions there.

In order to avoid false positives regarding to swap space, due to the zram device existence, we need to disable some checks:

mkdir -p /etc/systemd/system/systemd-logind.service.d/
cat <<-EOF | sudo tee /etc/systemd/system/systemd-logind.service.d/override.conf

mkdir -p /etc/systemd/system/systemd-hibernate.service.d/
cat <<-EOF | sudo tee /etc/systemd/system/systemd-hibernate.service.d/override.conf

Now you must reboot your computer, in order the steps until here take effect.

We also need to allow systemd-sleep system to read the swap file in SELinux . One option for allowing this is to make it fail. After that, use the audit2allow to do the white listing. Lets go step by step:

  1. Try to hibernate:

    # systemctl hibernate

    This should fail right now. Probably returning you to the display manager login. Also, logging details at /var/log/audit/audit.log .

  2. We can check the event happened with audit2allow inspecting the log, it will ouput something similar to this entry among others:

    # audit2allow -w -a
    type=AVC msg=audit(1630262756.460:2098): avc:  denied  { search } for  pid=26180 comm="systemd-sleep" name="swap" dev="dm-0" ino=256 scontext=system_u:system_r:systemd_sleep_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=0
    	Was caused by:
    		Missing type enforcement (TE) allow rule.
    		You can use audit2allow to generate a loadable module to allow this access.3.
  3. To see what rule we must allow, just type:

    # audit2allow -b
    #============= systemd_sleep_t ==============
    allow systemd_sleep_t unlabeled_t:dir search;

    The above rule should be the only one in the output. If not, we could be white listing other elements by accident.

  4. Instruct SELinux to allow further attemps by executing the following commands:

    # cd /tmp
    # audit2allow -b -M systemd_sleep
    ******************** IMPORTANT ***********************
    To make this policy package active, execute:
    semodule -i systemd_sleep.pp
    # semodule -i systemd_sleep.pp

We should be ready to hibernate now. Lets just try it by:

# systemctl hibernate

After resuming, the swap file should be deactivated, so continuing with the default zram swap device setup:

$ swapon
/dev/zram0 partition   8G   0B  100

As a bonus, you can enable gnome environment hibernate buttons by installing this extension.

Please check that all your devices state are properly restored after resuming.


If you are having unexpected problems, inspectioning the journal will be of help to see errors in systemd scripts:

journalctl -f
Copy link

LydiaMarieWilliamson commented Sep 11, 2021

Two things:
(1) without a clear explanation of precisely where you're getting the magic number 33GiB from and how it is computed, it's not possible to use this - or any other - part of your description.
(2) without an explanation of how or why LUKS encryption is being assumed, where in the description to follow this assumption is being used (and why), and what to do if you're not using LUKS, then it is likewise impossible to use your description.

Copy link

eloylp commented Sep 17, 2021

Two things:
(1) without a clear explanation of precisely where you're getting the magic number 33GiB from and how it is computed, it's not possible to use this - or any other - part of your description.
(2) without an explanation of how or why LUKS encryption is being assumed, where in the description to follow this assumption is being used (and why), and what to do if you're not using LUKS, then it is likewise impossible to use your description.

Thanks for your feedback @LydiaMarieWilliamson . I think the description already specifies an idea on how to calculate the space for the swap file. However, i added a comment in the code for the quick reader.

This guide assumes the default LUKS installation of Fedora34. I extended the description in order to clarify this. This is an option provided by the default Fedora34 installer. I think this guide should also work with an unencrypted setup, but still not tested. Please, if you do that, your feedback would make this better. Thanks !

Copy link

miXwui commented Oct 9, 2021

Thanks a ton for the write-up! Saved me a ton of time. A few additions:

I received this error in journalctl:

swapon: /swap/swapfile: insecure permissions 0644, 0600 suggested.

Fix: chmod 600 /swap/swapfile

Also, I'm not sure how your use of filefrag got you the correct offset on btrfs. Hibernation wasn't working, and I got these errors in journalctl:

kernel: PM: Cannot find swap device, try swapon -a
kernel: PM: Cannot get swap writer

Turns out on btrfs, the filefrag tool gives the incorrect offset. To calculate the correct offset I followed this:

I then updated resume_offset in grub, which worked! Note I'm not running LUKS encryption. So I can confirm it works with zram + btrfs, although I did have an issue where it got stuck from hibernation -- unsure why, might've been a fluke/my hardware.

Copy link

miXwui commented Oct 23, 2021

Another update:

The above systemd services enable/disable the swapfile before/after hibernate, but not before/after suspend-then-hibernate with HibernateDelaySec.

Ideally, during suspend-then-hibernate:

  1. the suspended system wakes from HibernateDelaySec
  2. swapfile is enabled and zram is disabled
  3. system proceeds with hibernation

Because 2. wasn't implemented, I received these errors:

Failed to find location to hibernate to: Function not implemented
Failed to prepare for hibernation: Function not implemented

A systemd service to prepare for hibernation (enable swapfile and disable zram) can be triggered before suspend and after all of suspend-then-hibernate completes.

A full suspend-then-hibernate operation can be:

  • system suspends, then wakes up before HibernateDelaySec e.g. via a manual power button press, and then remains on
  • system suspends, then wakes up to HibernateDelaySec, and then hibernates
  • system suspends, then wakes up to HibernateDelaySec, fails to hibernate, and then goes back into suspend

hybrid-sleep is also possible, but I won't get into that here.

Ideally the hibernation preparation script (enable swapfile and disable zram) triggers only before hibernation, which is in the middle of suspend-then-hibernate.

However, it's not possible to hook into suspend-then-hibernate with a system service: systemd/systemd#18092
since the suspend and hibernate stages are lumped into a singular suspend-then-hibernate service. I tried testing with ConditionEnvironment=SYSTEMD_SLEEP_ACTION=hibernate, but it didn't work since it's only possible for a service to start before or after the entire suspend-then-hibernate.

Thankfully it's possible to drop a script into /usr/lib/systemd/system-sleep that runs at different stages. The arguments passed to these scripts are:

  • $1 (first argument): pre or post
  • $2 (second argument): the action, either suspend, hibernate, hybrid-sleep, or suspend-then-hibernate
  • SYSTEMD_SLEEP_ACTION environment variable can equal suspend, hibernate, hybrid-sleep, or suspend-after-failed-hibernate (thanks to this pull request: systemd/systemd#18110)


So we should be able to enable swapfile and disable zram, in the script, at this stage:

  • $1=pre
  • $2=suspend-then-hibernate

But wait, not so fast!

When trying to enter hibernation after being woken up from HibernateDelaySec during suspend-then-hibernate at stage:

  • $1=post
  • $2=suspend-then-hibernate

This happens:

  1. (Line 303) Execute hibernate:
  2. (Line 218) Check hibernation location (in this case, the disabled swapfile):
  3. (Line 224) Because swapfile starts as disabled, the function returns an error:
  4. Line 237 is never reached to set SYSTEMD_SLEEP_ACTION=hibernate, so SYSTEMD_SLEEP_ACTION remains as suspend:
  5. (Line 307) Finally it proceeds to suspend-after-failed-hibernate, going back into suspend:

In summary, because the code checks for the hibernation location (in this case the presence of an enabled swapfile) before going into hibernation, and the swapfile starts as disabled, the entire operation errors. The system then goes back into suspend. SYSTEMD_SLEEP_ACTION stays as suspend and never changes to hibernate.

If the swapfile was enabled, then it'll successfully start hibernate, and SYSTEMD_SLEEP_ACTION would change to hibernate. But of course, the swapfile starts disabled because we use zram, and we want to enable the swapfile only if the system is going into hibernate.

So since it fails and goes straight back into suspend, we can't hook into:

  • $1=pre
  • $2=suspend-then-hibernate

The best we can do is hook into the stage before:

  • $1=post
  • $2=suspend-then-hibernate

This means that in suspend-then-hibernate, our script to enable swapfile and disable zram will always run post suspend (waking up from suspend), irregardless if the system:

  1. automatically wakes up from HibernateDelaySec to go into hibernation or
  2. resumes from suspend before HibernateDelaySec due to pressing the power button, opening the lid, etc.

If 2. happens, the swapfile will be enabled and zram disabled, which is not what we want when resuming from suspend to stay on (skipping hibernate). But a service can disable the swapfile after the full suspend-then-hibernate operation completes, so it's only a brief moment.

TLDR: It's not possible to enable swap and disable zram only before hibernate during suspend-then-hibernate. The best way I found during suspend-then-hibernate was to enable swap and disable zram post resuming from suspend, irregardless if the system is going to hibernate or is staying on (like when the power button is pressed before HibernateDelaySec kicks in).

The services/scripts:

[a] systemd service enabling swapfile and disabling zram before suspend during suspend-then-hibernate. Caveat: could cause an issue if swapfile is on and zram is off during suspend in suspend-then-hibernate.


Description=Enable swapfile and disable zram before hibernate during suspend-then-hibernate

ExecStart=/bin/bash -c "/usr/sbin/swapon /swap/swapfile && /usr/sbin/swapoff /dev/zram0"


Enable it with systemctl enable suspend-then-hibernate-preparation.service

[b] script to drop into /usr/lib/systemd/system-sleep/ enabling swapfile and disabling zram post suspend (when waking up from suspend) during suspend-then-hibernate

Better than [a], but requires adding a script in /usr/lib/systemd/system-sleep/.



if [ "$1" = "post" ] && [ "$2" = "suspend-then-hibernate" ] && [ "$SYSTEMD_SLEEP_ACTION" = "suspend" ]
    echo "suspend-then-hibernate (post suspend): enabling swapfile and disabling zram"
    /usr/sbin/swapon /swap/swapfile && /usr/sbin/swapoff /dev/zram0

Make sure the file is executable with chmod + x.

[c] systemd service disabling swapfile when suspend-then-hibernate completes, reversing [a] and/or [b]. zram seems to automatically enable on resume.


Description=Disable swapfile after resuming from hibernation during suspend-then-hibernate

ExecStart=/usr/sbin/swapoff /swap/swapfile


Enable it with systemctl enable suspend-then-hibernate-resume.service

Either combination of [a] + [c], [b] + [c], or [a] + [b] + [c] should work. I myself am running the current optimal solution [b] + [c].

Copy link

ixiotidi commented Feb 12, 2022


thanks for the useful instructions of setting up hibernation in fedora 34, however I am facing an issue on the last steps. Indeed after setting everything up and removing secure boot I was able to reach the last part with the audit instructions, however following it results in my system login out and then switching off the screen for a moment and back on in the login page. I tried to re-run the audit2allow -w -a command and I am getting the following output.

type=AVC msg=audit(1644684560.567:289): avc: denied { search } for pid=5135 comm="systemd-sleep" name="swap" dev="dm-0" ino=256 scontext=system_u:system_r:systemd_sleep_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=0
Was caused by:
Unknown - would be allowed by active policy
Possible mismatch between this policy and the one under which the audit message was generated.

            Possible mismatch between current in-memory boolean settings vs. permanent ones.

Does anyone experience the same issue? Thanks a lot in advance.

Copy link

manujchandra commented Mar 6, 2022


If we install NVIDIA proprietary drivers and follow the steps above, the drivers break and are no longer available. If we are on dedicated GPU the system will get stuck during boot.

Copy link

w4tsn commented Mar 14, 2022

Thank you very much for this! Saved me a lot of trouble.

I notice two things:

  1. You point grub2-mkconfig to /boot/efi/EFI/fedora/grub.cfg. While this works a default fedora installation expects the output of grub2-mkconfig to go into /boot/grub2/grub.cfg while the file /boot/efi/EFI/fedora/grub.cfg includes the other one
  2. Using audit2allow -a -M systemd_sleep might result in unexpected behavior since -a stands for all and might add more rules to the module than you'd expect or even straight up fail. It is safer to do a clean reboot and use audit2allow -b after the systemctl hibernate instead or even provide the exact entry from audit.log to audit2allow -i INPUT where INPUT is some file or log line containing only the relevant audit line

A also had to use the approach in to calculate the offset. Thanks @miXwui for pointing that out!

Good to know: this also works if LUKS is configured for automatic decryption via TPM2.

Copy link

w4tsn commented Mar 15, 2022

@eloylp what do you think about posting this on the FedoraMagazine? I think this would make a great contribution with a lot of reach there. I'll write an article under attribution if you wish and permit it. Only "problem" or inconvenience I see right now is that you have to grab some unverified (small) piece of source code, compile it and execute it as root to get the correct physical address on btrfs. Maybe there is another way, but I did not find it yet

Copy link

eloylp commented Mar 26, 2022

Hello guys. It was a pleasure to read all your comments so far. Many thanks for all your contributions ! I fixed the guide to reflect all your findings. I hope its better now for new readers. Allow me to reply to all of you on this comment and summarise the changes:

  • Update the guide to point out that works for Fedora34/35.

  • As many of you experimented problems with the filefrag method for calculating the swap offset, i just changed that section to point to the archwiki section, which seems is the most reliable way. It is still a mistery for me how is possible that my setup is working fine, after using the filefrag method. Lets see if i find some time to properly understand what happened.

  • @miXwui , thanks a lot for all your contributions. Great detailed explanations/investigations ! Not only i learnt a lot, now im also happily running [B] + [C] . I added a link to your comment in the guide, so new readers can benefit from it as they read.

  • @manujchandra thanks for noticing the problem with the nvidia drivers. I was able to test this on a new desktop i bought. On my case its partially working: It correctly resumes, but seems GNOME is having rendering problems. Maybe the state of the graphic card is not correctly resumed for some reason. I need to investigate a bit more this, but cannot guarantee at what point in time. Until we find a solution, i just wrote an advice at the beginning of the guide warning this. The way i installed the nvidia drivers was through the rpm fusion non free repo. Heres the guide i followed, just in case someone else wants to continue investigations.

  • @w4tsn many thanks for your contributions ! Just modified the guide to:

    • Fixed the grub2-mkconfig command. Now /boot/grub2/grub.cfg is used as destination for the output.

    • Now the guide only allows the systemd security exception in selinux.

    • Seems this guide works perfectly fine with most setups, encrypted/unencrypted. Just clarified that at intro.

      Regarding to the post in FedoraMagazine , of course ! go for it. Lets just put a link here whenever its done, so the information is connected.

  • @ixiotidi thanks to @w4tsn findings (see point above), now we modified the guide to only allow the needed systemd rule in selinux. Please follow that part again and check if that solves your problem.

Copy link

jorp commented May 28, 2022

Hi all, just wanted to share a playbook I made back in August for Fedora 34. I also have a corresponding blog post here.

Copy link

ameeno commented Jun 10, 2022

Hi all, just wanted to share a playbook I made back in August for Fedora 34. I also have a corresponding blog post here.

Thanks for this playbook. its useful however it adds swap to fstab, (which this guide does not do) also your calculations for swap file size is not taking into account the zram size and so is creating a swapfile of 16gb for 16gb of ram.

it also isnt doing the suspend-then-hibernate changes nor is it adding the selinux allows. so not got this working yet (can hibernate but not resume) very weird

Copy link

pietryszak commented Jun 29, 2022

I create a bash script, based on @jorp solution and information on this gist provided by @eloylp and comment by @miXwui.
It's test version and I test it at vm on VMWare. Works fine. Please help me with test, if you have a machine to do this. Please remember that something can go wrong.

Copy link

krokwen commented Jul 4, 2022

just finished setup for F36.
new issues has arrived.

This happens when hibernate-resume service is enabled:
Failed to find location to hibernate to: Function not implemented
Failed to prepare for hibernation: Function not implemented

Because somehow it runs before hibernation itself, and hibernation loses hibernation location.
Finally, just disabled this service, and hibernation become working.

But now i have to fix disabling swap-file on resume...

After I fix this, I will start working on suspent-then-hibernate and hybrid-sleep, and will pack everything as rpm.

Thank you for your guide, it was working flawlessly on my full-amd rog 15 on F34.

For now tested on r5 5500u asus zenbook uw424u.

Current script for hibernation setup (WIP); It's fully automated; used grubby, because in f36 we no longer have a single grub.cfg, but slices per-core; automated generation of selinux policy, and usage of bmp C program.

just tested hibernate-resume one more time, and it works...

Copy link

quiteBold commented Sep 7, 2022

Hi all.

I just tried on Fedora 36 with an encrypted drive.

$ sudo systemctl hibernate
Failed to hibernate system via logind: Sleep verb "hibernate" not supported
$ sudo journalctl -b
kernel: Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7

Seems like the kernel is locked regarding using an unencrypted swap file, though the swap-file lies in an luks-encrypted volume. Does anyone have a hint / solution at hand?
(I use secure boot and want to stay with it)

Thanks in advance

Copy link

miXwui commented Sep 7, 2022

Hey y'all, it's been a while! (in this comment: hibernation/resume debugging tips + my issue and solution)

@eloylp and @w4tsn I'm glad my info helped, and @w4tsn I happened upon your Fedora Magazine article -- great article! Next time I need to setup hibernation, I will either refer to that and/or test out @pietryszak's script.

So a couple months ago, my system (Framework Laptop i7-1165G7) started freezing on hibernation resume (either black screen with just an underscore _ or sometimes it would show the kernel version, something like Fedora_5.19.6-200.fc36.x86_64 (that's not exactly what it said). Not every time, but a lot of the times.

It seemed like it was hard freezing when loading the kernel, so I couldn't Magic SysRq or drop/switch into a terminal.

I finally did some digging today, and happened upon this great article from Intel on best practices to debug suspend/hibernate issues:
Note sections 2.1 initcall_debug and 2.3 ignore_loglevel and also this answer.

So I added this to my kernel parameters:

ignore_loglevel=1 initcall_debug=1

And upon system boot/hibernation resume/kernel loading, debug logs appear. I was able to see where hibernation resumption got stuck -- the last few lines printed were:

intel_ish_ipc 0000:00:12.0: [ishtp-ish]: Timed out waiting for FW-initiated reset
intel_ish_ipc 0000:00:12.0: ISH: hw start failed.
initcall ish_driver_init+0x0/0x1000 [intel_ish_ipc] returned 0 after 219091 usecs

So I did some digging, and found these (just dumping links here for reference to this particular issue and feel free to ignore)

To fix the issue, I simply blacklisted the intel_ish_ipc module by adding this kernel parameter:


though it should also work by adding this to e.g. /etc/modprobe.d/blacklist.conf (or best-practice-naming as /etc/modprobe.d/intel_ish_ipc.conf

blacklist intel_ish_ipc

After blacklisting, my system no longer freezes on that module and resumes normally!
Though it seems that the intel_ish_ipc module still loads afterwards since it's a dependent module for intel_ishtp:

lsmod | grep ish
intel_ishtp            61440  2 intel_ishtp_hid,intel_ish_ipc

Hopefully this helps others know how to more thoroughly debug hibernation resumption issues and/or solve the issue with a faulty module. For anyone wondering, I'm still not sure if my issue was caused by a kernel regression, a Framework BIOS update, a Fedora update, or something else.
Edit: though from seeing others have issues recently on other systems, I tend to believe it stems from a recent kernel regression

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment