Change:
When using to create and activate a swapfile on btrfs, it fails with messages like this: The kernel says: I am not very familiar with Nix, but after reading the corresponding and , it seems that creating a swapfile on btrfs requires disabling copy-on-write for that file (via ) as well. However, doesn't do it currently. BTW, manually creating the swapfile and letting NixOS activating it works fine for me. to ( is supposed to be mounted as btrfs). . Swapfile is created and activated without any issue. There is a workaround on NixOS Discourse: system: host os: multi-user?: sandbox: version: channels(root): nixpkgs:
I have the same issue. I already tried disabling copy-on-write for the partition but it still fails: I also still get the warning like even though I the attribute to my mount options (afterwards). My current swap configuration can be found .
Mounting with attribute won't work because: [^1]:
I came up with this yesterday, which seems to work (or at least you can follow the general pattern): I have as a separate subvolume, nothing else on it. Once you've run this to create the swapfile, it seems to mount and function properly from there on out. But yes, it would be nice if this were taken care of automatically. EDIT: Fix link
brilliant, that helped me solve my issue. While your script didn't work out for me, I was able to adjust it so it did. I noticed the following warning when running your script: So, I searched for this and found the following note on ArchWiki: — Replacing with made the warning disappear for me but running still failed with . Then I compared the script to what I've used in and noticed that I used instead of to create the empty swapfile. The full script I ended up with:
Thanks for the update. I originally used , but switched after reading that should be much faster (and have to admit I can't remember if I tested that change yet -- thank you for the feedback). I've switched it back to .

Discussion:
device = mkOption { example = "/dev/sda3"; <del> type = types.str; </del> <ins> type = types.nonEmptyStr; </ins> description = lib.mdDoc "Path of the device or swap file."; };

}; config = mkIf ((length config.swapDevices) != 0) { <ins> assertions = map (sw: { assertion = sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null; message = '' You cannot use swap device "${sw.device}" with randomEncryption enabled. The UUIDs and labels will get erased on every boot when the partition is encrypted. Use /dev/disk/by-partuuid/… instead. ''; }) config.swapDevices; warnings = concatMap (sw: if sw.size != null && hasPrefix "/dev/" sw.device then [ "Setting the swap size of block device ${sw.device} has no effect" ] else [ ]) config.swapDevices; </ins> system.requiredKernelConfig = with config.lib.kernelConfig; [ (isYes "SWAP")

# Create missing swapfiles. systemd.services = let createSwapDevice = sw: <del> assert sw.device != ""; assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-uuid"  sw.device); assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-label" sw.device); </del> let realDevice' = escapeSystemdPath sw.realDevice; in nameValuePair "mkswap-${sw.deviceName}" { description = "Initialisation of swap device ${sw.device}"; wantedBy = [ "${realDevice'}.swap" ]; before = [ "${realDevice'}.swap" ]; <del> path = [ pkgs.util-linux ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup; </del> <ins> path = [ pkgs.util-linux pkgs.e2fsprogs ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup; environment.DEVICE = sw.device; </ins> script = '' ${optionalString (sw.size != null) '' <del> currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 )) if [ "${toString sw.size}" != "$currentSize" ]; then dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size} </del> <ins> currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 )) if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then # Disable CoW for CoW based filesystems like BTRFS. truncate --size 0 "$DEVICE" chattr +C "$DEVICE" 2>/dev/null || true dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} </ins> chmod 0600 ${sw.device} ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"} fi

strongswan-swanctl = handleTest ./strongswan-swanctl.nix {}; stunnel = handleTest ./stunnel.nix {}; sudo = handleTest ./sudo.nix {}; <ins> swap-file-btrfs = handleTest ./swap-file-btrfs.nix {}; </ins> swap-partition = handleTest ./swap-partition.nix {}; sway = handleTest ./sway.nix {}; switchTest = handleTest ./switch-test.nix {};

<ins> import ./make-test-python.nix ({ lib, ... }: { name = "swap-file-btrfs"; meta.maintainers = with lib.maintainers; [ oxalica ]; nodes.machine = { pkgs, ... }: { virtualisation.useDefaultFilesystems = false; virtualisation.bootDevice = "/dev/vda"; boot.initrd.postDeviceCommands = '' ${pkgs.btrfs-progs}/bin/mkfs.btrfs --label root /dev/vda ''; virtualisation.fileSystems = { "/" = { device = "/dev/disk/by-label/root"; fsType = "btrfs"; }; }; swapDevices = [ { device = "/var/swapfile"; size = 1; # 1MiB. } ]; }; testScript = '' machine.wait_for_unit('var-swapfile.swap') machine.succeed("stat --file-system --format=%T /var/swapfile | grep btrfs") # First run. Auto creation. machine.succeed("swapon --show | grep /var/swapfile") machine.shutdown() machine.start() # Second run. Use it as-is. machine.wait_for_unit('var-swapfile.swap') machine.succeed("swapon --show | grep /var/swapfile") ''; }) </ins>