Hibernate not working with swapfile

I am trying to set up hibernate (and suspend-then-hibernate eventually) with i3wm.

My swapfile lives on my home partition and is confirmed working as swap, so the issue is purely hibernation I think. I have tried every solution on the wiki and here with no success.

In particular, I have tried setting up swapDevices and using

boot.initrd.systemd.enable = true;

as well as also setting kernelParams and resumeDevice.

Regardless, systemctl hibernate always gives

Call to Hibernate failed: No such file or directory

Output of nixos-version: 25.11.20250819.2007595 (Xantusia)

Any suggestions are welcome and appreciated, thanks!

Use the official wiki: Power Management - NixOS Wiki

That said, could you share your actual configuration? Making stage1 use systemd isn’t necessary but also not harmful.

Hibernation should just work if you have the swapfile set up correctly, which it doesn’t look like you do. Tell us about your partitioning scheme / swapfile location etc too, assuming you don’t have that encoded in your config.

I’m also going to say that swap partitions are generally simpler to configure, and the benefits in terms of variable size are negligible in 2025; you want to have that space reserved for hibernating your full memory anyway, so there isn’t any benefit in practice. Swap files cause issues with some file systems, too.

2 Likes

Thanks for the suggestions! I just tried the official wiki’s setup (and a few additional whack-a-mole fixes based on the errors it gave).
Note: when I rebooted it stalled at waiting for device /home/swapfile to become available.... for a while which is new and I assume unintended.

My partition is as follows, with no encryption etc.

NAME    FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
nvme0n1
├─nvme0n1p1
│       ext4   1.0   root  556a803f-3464-4f4b-a098-b934d30c2c72   64.4G    28% /nix/store
│                                                                              /
├─nvme0n1p2
│       ext4   1.0   home  df1f14b0-a8f5-4b7c-9fd7-2b80136fc36b   81.5G    72% /home
└─nvme0n1p3
        vfat   FAT32       3BFD-6C80                             775.5M    24% /boot

My configuration is currently

  boot.kernelParams = [
    "mem_sleep_default=deep"  # for deep suspend
    "resume=/home"  # for hibernation
    "resume_offset=90144768"
  ];

  swapDevices = [{
    device = "/home/swapfile";
    size = 30*1024;  # MB
    options = ["discard"];
  }];

  boot.resumeDevice = "/home/swapfile";

The kernelParams options I set because the wiki suggests setting the offset as a kernelParam when using swapfiles, and then when I did that systemctl complained

Call to Hibernate failed: Invalid resume config: resume= is not populated yet resume_offset= is

so then I also set resume=/home but it’s still giving this same error when trying to hibernate. I have rebooted and confirmed that /proc/cmdline contains both options.

So, resume= sets the device the kernel is supposed to resume from, and resume_offset= sets the offset in the device where the swap header is located. You’re asking the kernel to read the block device /home and resume from ~90MB into that block device. You also don’t need to set resume since that’s done by boot.resumeDevice.

/home is not a block device, and an offset of ~90MB into a directory makes no sense. It’s unsurprising this doesn’t work.

Use the block device that /home is on, and point the kernel at the offset of the swapfile in there.

You can get the offset of the file with filefrag -v /home/swapfile - you’re looking for the physical_offset.

So in other words:

{
  boot = {
    # Ideally, replace this with `/dev/disk/by-uuid/<UUID>` so the device
    # doesn't randomly change; use `lsblk -o +UUID` to find it
    resumeDevice = "/dev/nvme0n1p2";
    kernelParams = [ "resume_offset=<physical offset of the file>" ];
  };
}

This is assuming you’re using e.g. ext4, if you use other file systems, ideally just don’t use a swapfile. I know you are, but I want to repeat that because it’s a major data destruction risk at least on btrfs.

Also, as an aside, putting a file like this in /home is very weird, use /swap, or maybe a subdirectory of /var.

Of course, this is also quite unreproducible. NixOS will create swapfiles that don’t exist automatically for you, but getting the offset in the filesystem can only be done at runtime, so you’ll never get a fully reproducible system this way.

Really, the more I think about this the more I disrecommend using swap files. They’re anything but noob-friendly, inherently cause reproducibility issues, technically less performant due to the possibility of fragmentation, and an inherent data destruction risk with certain file systems. The only benefit is that they’re easy to resize, but that inherently causes the very fragmentation that makes them imperformant, so you should never actually do that (and nobody tells you this…). Plus, this is not the early 2000s, resizing filesystems is quite possible. If this is something you find yourself worried about just use LVM volumes.

Okay, here is my current configuration (which is basically what I had before my first post here)

  boot.resumeDevice = "/dev/disk/by-uuid/df1f14b0-a8f5-4b7c-9fd7-2b80136fc36b";
  boot.kernelParams = [
    "mem_sleep_default=deep"  # for deep suspend
    "resume_offset=90144768"
  ];
  # swapDevices stuff remains the same

but same error:

Call to Hibernate failed: No such file or directory

Are there things I can instrospect into (at perhaps the systemd level) to see what’s actually going wrong and maybe have a better idea of what to do?

(FWIW, I wanted to use LVM but the Calamares installer for NixOS did not comply at all and I eventually gave up.)

I agree with TLATER: swap files really should not be used for hibernation. They’re handy when you quickly need to add more memory to run some intensive task, but having the kernel locate the file with an offset is a hack at best: it’s file-system dependent, it’s not reproducible and not stable.

1 Like

Hmm, any more detail in journalctl -e or dmesg? Make sure to use sudo when calling systemctl hibernate, too.

The manual install is way more flexible and less likely to cause you headaches. It’s not actually complicated, at least not more so than setting up a swapfile. There’s even nmtui to make wifi connections easy these days.

No, I’ve looked through those logs and haven’t been able to find much except the actual call to systemctl (in journalctl), but no errors seem to be logged.

Is all the setup in my configuration.nix resulting in the generation of some files or variables which I can check to confirm correctness? I know this is the case for some other variables, like systemd.sleep.extraConfig, but I’m finding it hard to debug my current issues when things appear correct at the nix level but lower levels are erroring out.

I’m thinking maybe I’ll turn up the systemd log level and then try this again?

Noted about the manual install, thanks.

I upped my systemd and systemd.logind log levels to debug, and here’s the closest I could find to an error:

Sep 18 22:35:21 strassen systemd[1]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/systemd1/unit/suid_2dsgid_2dwrappers_2eservice interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=2649 reply_cookie=0 signature=sa{sv}as error-name=n/a error-message=n/a
Sep 18 22:35:21 strassen systemd[1]: suid-sgid-wrappers.service: Changed dead -> start
Sep 18 22:35:21 strassen systemd[1]: Starting Create SUID/SGID Wrappers...
Sep 18 22:35:21 strassen systemd[1]: systemd-hibernate-clear.service: ConditionPathExists=!/etc/initrd-release succeeded.
Sep 18 22:35:21 strassen systemd[1]: systemd-hibernate-clear.service: ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67 failed.
Sep 18 22:35:21 strassen systemd[1]: systemd-hibernate-clear.service: Starting requested but condition not met. Not starting unit.
Sep 18 22:35:21 strassen systemd[1]: systemd-hibernate-clear.service: Job 3532 systemd-hibernate-clear.service/start finished, result=done
Sep 18 22:35:21 strassen systemd[1]: Clear Stale Hibernate Storage Info was skipped because of an unmet condition check (ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67).
Sep 18 22:35:21 strassen systemd[1]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/systemd1/unit/systemd_2dhibernate_2dclear_2eservice interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=2650 reply_cookie=0 signature=sa{sv}as error-name=n/a error-message=n/a
Sep 18 22:35:21 strassen systemd[1]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/systemd1/unit/systemd_2dhibernate_2dclear_2eservice interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=2651 reply_cookie=0 signature=sa{sv}as error-name=n/a error-message=n/a
Sep 18 22:35:21 strassen systemd[1]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/systemd1 interface=org.freedesktop.systemd1.Manager member=JobRemoved cookie=2652 reply_cookie=0 signature=uoss error-name=n/a error-message=n/a

But this seems to be more about cleanup for hibernation (which never happened in the first place) failing, so I’m not sure if it’s helpful.

I’m unsure of what to do. I consider not being able to use a swapfile for hibernation to be a bug, even if the folks here seem not to advise it.

The fact that I keep getting a resume= not set error when I explicitly set it in the kernelParams and have confirmed it’s existence in /proc/cmdline seems perplexing.

Uh, we need to simplify. Try just configuring a swap file in swapDevices and remove any kernel params or anything else, just leave boot.initrd.systemd.enable = true;. With this setup, on UEFI systems, systemd is supposed to automatically detect the swap file’s device and offset and save them in EFI variables. If you set any of it manually you’ll be overriding the automatic detection. Ideally there is no resume= or resume_offset= kernel params, and systemd handles this stuff on its own. All you should need is the swapfile configured as a swapfile (assuming the system is UEFI).

I mean I kinda agree in the sense of “I don’t like it” but I do fully expect it to work. The kernel and these file systems are supposed to make it so. It can take some special effort though; for instance I know btrfs has special commands you need to use to allocate a swapfile.

Okay, I’ve stripped back to the suggested setup:

  swapDevices = [{
    device = "/home/swapfile";
    size = 30*1024;  # MB
    options = ["discard"];
  }];
  boot.initrd.systemd.enable = true;

I am still getting

Call to Hibernate failed: No such file or directory

I see my call to hibernate in the journal, but no obvious misconfiguration errors.

From this article on ArchWiki it seems hibernation with a swapfile in /home specifically is unsupported by systemd. It also has links to some workarounds.
https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate#Swap_file_in_/home

4 Likes

Unrelated to this thread but bless the Arch Wiki, akin to the modern Library of Alexandria :grin:

1 Like

Phew, that’s quite the edge case. Funny that my suggestion of not putting the swapfile in /home because that’s weird would likely have solved this ;p

ooi, where does the code for this live? I can only find how this works with the traditional stage1, and I can’t find any fancy .swap unit generation. With the traditional stage1 resume= and resume_offset= are very much required.

The systemd manual definitely says it can do this, though, I’d just like to know how that’s put together on NixOS. That way I can actually tell folks how to use the systemd initrd in the future ;p

It’s all in systemd. We literally implement nothing because the systemctl hibernate command is the code that does this logic. It looks for whatever swap is active, finds the drive it lives on, finds the offset if it’s a swapfile, and stores that info in an EFI variable that’s read by systemd-hibernate-resume-generator in systemd stage 1.

2 Likes

Thanks all for the help (and thanks Arch Wiki)! The swapfile being on /home was indeed the problem. This is strange and slightly ironic, since I had a swapfile on /home and hibernate working on my previous Arch setup as far as I remember.

In any case, I can’t really afford to put my swapfile on the root partition (I didn’t budget for it when allocating during install), so my final working setup is as follows:

  swapDevices = [{
    device = "/home/swapfile";
    size = 30*1024;  # MB
    options = ["discard"];
  }];

  systemd.services."systemd-logind".serviceConfig = {
    ProtectHome = "read-only";  # allows logind to read /home
  };

Happily hibernating,
G

Note that you also need to use the systemd initrd for this to work, so to be clear for future readers, the summary is:

{
 swapDevices = [{
    device = "/swap";  # The OP used `/home/swapfile`, which is inadvisable
    size = 30*1024;  # MB
    options = ["discard"];
  }];

  # Only required when using a subpath of `/home` to store your swapfile; hence
  # doing so is inadvisable.
  # systemd.services."systemd-logind".serviceConfig.ProtectHome = "read-only";

  # Systemd is used to automatically resolve the swapfile location when
  # hibernating. This means we *must* use systemd in the initrd.
  boot.initrd.systemd.enable = true;
}

Using a swap partition is probably preferable over a swap file most of the time. Consider re-partitioning instead of using swapfiles.

If you’re doing this because you’re worried that you might change your mind about partition sizes, consider looking into LVM to make resizing partitions easier, and note that growing a swap file/partition can cause performance loss due to fragmentation.

2 Likes

As well as LVM, it’s worth saying that you can actually shrink btrfs file systems using btrfs filesystem resize live and I did this not so long ago because I decided I wanted a swap partition (previously I was no swap). You just need enough free space. I was very sure I was going to brick my system, but to my surprise, a resize + partition adjustment and everything “just worked”.