[HowTo] Enable and configure hibernation with BTRFS

But it is, see

In the context where an assignment statement is assigning a value to a shell variable or array index (see Arrays), the ‘+=’ operator can be used to append to or add to the variable’s previous value.

Using witespace “around” strings to append is commonplace to not mess up any previous content.

1 Like

@freggel.doe thanks! I restored that command in the wiki. But I already fixed that file manually on my, still hibernation doesn’t work. Might have found the culprit, see my latest message above you. Not sure how to fix.

Hello @zilexa

I will check/test the script again with the latest xfce minimal iso (that is the one which I use for testing in a vm).

I will update this post, when finished this weekend.

Thanks @megavolt I do believe every command in the guide now works correctly.

Why I get this error is beyond me.

I have had this error on older systems since almost half a year now. No matter how many times I configure this, always end up with the same error.
Now that I tested your new version on a clean new install I’m convinced the issue is not isolated to my systems.

Hello @zilexa,

I guess I have found the issue and it was my mistake. I forgot that I didn’t use filefrag on my setup :man_facepalming:

Normally it is a “setup and forget” configuration for me and well you know, I have forgotten it. :roll_eyes:

Thanks @zomberman for mentioning it. Sadly you did not edit the wiki post. I almost over read it.

1 Like

With the new & updated wiki, my script is now working again, copy pasted the wiki and also enabled a few related stuff, tested & verified:

manjaro-gnome-post-install/btrfs-hibernation.sh at main · zilexa/manjaro-gnome-post-install · GitHub

The script will:

  1. Automatically perform the actions in this wiki, creating the root subvolume for swap, swap file and configuring hibernation.
  2. Additionally, enable "suspend-then-hibernate", the most modern way of standby! suspend-then-hibernate will be the default suspend method for suspend, always hibernating 2 hours after suspend.
  3. Power-off key will skip suspend, instead hibernate the system immediately.
  4. (If a laptop) lid close will “suspend-then-hibernate”.

What is suspend-then-hibernate?
The system will go quickly into standby and wake up quickly if woken by the user within 2 hours. Otherwise, it will automatically wake up and hibernate (write to swap & shutdown completely), saving battery power and preventing you from losing data when battery dies.
When you wake the system after 2 hours, wake up can take slightly longer as it will wake up from hibernation.

Great guide. Very well written and helpful. However I want to put a HUGE WARNING.

Before “step 2” of the Hibernation enabling with (mkinitcpio -P etc). Check that /etc/mkinitcpio.conf looks ok.
If it looks like this:

HOOKS=(base udev autodetect keyboard keymap modconf block filesystems fsck)
HOOKS+=" resume "

Then it’s not ok and running mkinitcpio -P will print errors “ERROR: Hook 'base resume ’ cannot be found” and now your system is unable to boot… (so don’t do this or don’t reboot until you fix it).

Instead, edit it like this, moving the resume to it’s proper place:

HOOKS=(base udev autodetect keyboard keymap modconf block filesystems fsck resume)

Then you can continue with “step 2”, but I would really do it like this instead:

  1. Backup grub.cfg sudo cp /boot/grub/grub.cfg /boot/grub/grub.cfg.bak
  2. Run sudo grub-mkconfig -o /boot/grub/grub.cfg first. If you want, diff the new conf with the backup to see that the changes are applied.
  3. Then you can run sudo mkinitcpio -P. If gives you errors it’s important to fix them. Revert your config changes and then run sudo mkinitcpio -P again until it works. Warnings are fine though (especially “Possibly missing firmware for module” which are common).
  4. Reboot

Ah well thanks, you are right. A string is not an array. I guess arrays are now default on new installations. I fixed it.

1 Like

Thanks, @megavolt, for the guide, as this helped me get started.

Here are a few observations, which are most likely relevant in time, i.e., 6.1.12-1-MANJARO and systemd 252 (252.5-1-manjaro).

  1. Steps to create swap file on BTRFS work flawlessly
  2. echo -e "GRUB_CMDLINE_LINUX_DEFAULT+=\" resume=UUID=$swp_uuid resume_offset=$swp_offset \"" | sudo tee -a /etc/default/grub appends the changes to the end of /etc/default/grub
  3. echo -e "HOOKS+=( resume )" | sudo tee -a /etc/mkinitcpio.conf appends the changes to the end of /etc/.conf

@zilexa, the additional changes you allude to are mostly unnecessary now. Except for HandlePowerKey=hibernate, the other settings are default values, and compiled into the kernel. Likewise, the symbolic link breaks hibernation. Indeed, it is my understanding that systemd 252 introduced some changes to power management*, and this will change again in 253.

The only addition I made was to add the Hibernate Status Button extension to GNOME.

In summary, systemctl suspend, closing the lid, or using the updated power menu will enter deep sleep. systemctl hibernate suspends to disk immediately, as does the hibernate menu option.

Finally, systemctl suspend-then-hibernate will write data to disk and NVM, and put the laptop to sleep. I also observe that with very low battery, GNOME will initiate suspend, but this action implements suspend-then-hibernate. I’ve not had the patience or time to fully test, but I understand that a timer (defined by HibernateDelaySec=120min) will check power, and then hibernate.

If power is lost beforehand, the state is already saved to disk. This seems to work when I initiate shutdown using the power button.

I trust this information is useful.


Be careful, disabling CoW for Swapfile will be broken by running Btrfs balance.

I have already tested Swapfile on Btrfs with NoCOW in VM a few days ago, there are some issues:

  • The physical offset of Swapfile is changed after Btrfs balance:

Before balance:

$ sudo filefrag -v /swap/swapfile
Filesystem type is: 9123683e
File size of /swap/swapfile is 2147483648 (524288 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    6241536..   6241536:      1:            
   1:        1..  262143:    6241537..   6503679: 262143:             unwritten
   2:   262144..  524287:    5979392..   6241535: 262144:    6503680: last,unwritten,eof
/swap/swapfile: 2 extents found

After balance:

$ sudo filefrag -v /swap/swapfile
Filesystem type is: 9123683e
File size of /swap/swapfile is 2147483648 (524288 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    9338112..   9338112:      1:            
   1:        1..  524287:    9338113..   9862399: 524287:             last,unwritten,eof
/swap/swapfile: 1 extent found

See physical_offset of Swapfile does not match resume_offset for hibernation in kernel parameter.

  • Try to enable swapon but it fails:
$ sudo swapon /swap/swapfile
swapon: /swap/swapfile: swapon failed: Invalid argument

Because I got a warning dmesg-message by the automatic notification popup (btrfs-desktop-notification-git):

kernel: BTRFS warning (device dm-0): swapfile must not be copy-on-write

It seems that the swapfile is still running in CoW after disabling CoW and running balance.

The current kernels 5.15, 6.1 and 6.2 are also affected.

I’ve read some messages from Reddit, some Btrfs users don’t recommend using NoCOW on Btrfs.

@tristanw thanks! I have to look into this because my laptop is no longer hibernating since a few updates ago. So something in my original steps combined with the last few updates caused this.

@Zesko Does this mean we simply cannot use hibernation with BTRFS anymore? That would be weird. Or simply do not use Balance on the drive that contains the hibernation file? I also understand balance should not be needed by default unless you have a very clear reason to do it.

You can continue to use the hibernation, but do not use Balance, otherwise Balance breaks the swapfile as NoCOW.

Personally, I always use Swap partition. Btrfs lets you shrink Btrfs partition and you can create a new partition for Swap or EXT4 with Swapfile.

Ok, you mean the nodatacow option? I just added it, just to be sure. Normally setting

sudo chattr +C /swap/swapfile

is quite enough. The file will not be compressed and not COW, so that file should be excluded when balance is running. Actually, I balance my SSD/NVME sometimes and didn’t see your problem. Hibernation works as expected.

Did you both? Set +C on the file and added nodatacow to options fsttab? Were your observations made with these assumptions?

Yeah I know, it is kinda ugly to add it at the end of the file, but is functional, since it expands the variable. My idea was to create easy steps and avoid being very complicated.

I already added nodatacow for the subvolume @swap in fstab and created a swapfile with the commandline:

$ sudo btrfs filesystem mkswapfile /swap/swapfile

See the Btrfs document: Swapfile — BTRFS documentation

If a btrfs balance changes the offset and therefore hibernation, then I would assume that there is a bug when calling this mkswapfile function. I did it recently again on a Mini-PC from HP like explained on top and a balance does not lead to malfunctions.

Possible that the attribute +C was set after creating the file and filling the file with dd, then there will be COW extents and therefore changes will occur on btrfs balance.

I tried to use chattr +C swapfile in VM today, the problem also appeared when swapoff first, then balance, then swapon, it fails.

It has nothing to do with the activation of then swapfile. Look, to make more understandable:

Don’t do:

truncate -s 0 swapfile
fallocate -l 10G swapfile
chattr +C swapfile
touch swapfile
dd if=/dev/zero of=/swapfile bs=1M count=10000 status=progress
chattr +C swapfile

But do:

truncate -s 0 swapfile
chattr +C swapfile
fallocate -l 10G swapfile
touch swapfile
chattr +C swapfile
dd if=/dev/zero of=/swapfile bs=1M count=10000 status=progress

Gotcha? That was also one of my traps at the beginning.

[root@test-btrfs /]# mkdir swap
[root@test-btrfs /]# mount /dev/sda2 swap/ -o subvol=/@swap,nodatacow,noatime
[root@test-btrfs /]# truncate -s 0 swap/swapfile
[root@test-btrfs /]# chattr +C swap/swapfile 
[root@test-btrfs /]# fallocate -l 2G swap/swapfile 
[root@test-btrfs /]# chmod 600 swap/swapfile 
[root@test-btrfs /]# swapon /swaps/swapfile
swapon: cannot open /swaps/swapfile: No such file or directory
[root@test-btrfs /]# mkswap swap/swapfile 
Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
[root@test-btrfs /]# cd swap
[root@test-btrfs swap]# swapon swapfile
[root@test-btrfs swap]# btrfs balance start / --full-balance
[root@test-btrfs swap]# swapoff swapfile
[root@test-btrfs swap]# swapon swapfile
swapon: /swap/swapfile: swapon failed: Invalid argument

1 Like

Alright, tested it on my system and get the same result. So --full-balance also moves files regardless of the attribute, which are marked as nodatacow +C and yeah it remains as marked, but does not work.

Very interesting. Also noted in the manual:

       start [options] <path>
              start  the  balance  operation according to the specified filters, without any filters the data and metadata from the whole filesystem are
              moved. The process runs in the foreground.

                 The balance command without filters will basically move everything in the filesystem to a new physical location  on  devices  (i.e.  it
                 does not affect the logical properties of file extents like offsets within files and extent sharing).  The run time is potentially very
                 long, depending on the filesystem size. To prevent starting a full balance by accident, the user is warned and has  a  few  seconds  to
                 cancel the operation before it starts.  The warning and delay can be skipped with --full-balance option.

              Please  note  that  the  filters must be written together with the -d, -m and -s options, because they're optional and bare -d and -m also
              work and mean no filters.

                 When the target profile for conversion filter is raid5 or raid6, there's a safety timeout of 10 seconds to warn users about the  status
                 of the feature

I use normally: btrfs balance start -dusage=90 / and that has zero effect on the swapfile.

I conclude: Never use a full-balance on a machine, since it will ignore +C Attributes.

Note: should be added as a note in the wiki.

That’s interesting, btrfs-assistant GUI uses --full-balance to break swapfile, that is why other Btrfs users got this issue.

1 Like