zramSwap.enable=true does nothing

The zram module doesn’t seem to working for me. I may dig a little deeper later, but posting this in case anyone has suggestions about what might be wrong.

I just added this configuration.nix and rebooted:

  zramSwap = {
    enable = true;
    algorithm = "zstd";
  };

After reboot, swapon -s only lists the devices I had before, and there are no devices named /sys/class/block/zram*.

Poking around, I found something called zram-init-zram0.service and decided to play with it. Initially, zram doesn’t appear anywhere in the output of systemctl, and the status looks like this:

[root@angel:~]# systemctl status zram-init-zram0                                                                             
● zram-init-zram0.service - Init swap on zram-based device zram0                                                             
   Loaded: loaded (/nix/store/vrfmjfr3qg2sq75dc1v6amha3xpba37g-unit-zram-init-zram0.service/zram-init-zram0.service; enabled>
   Active: inactive (dead)

Then after I manually start it, the status output looks different:

[root@angel:~]# systemctl start zram-init-zram0

[root@angel:~]# systemctl status zram-init-zram0
● zram-init-zram0.service - Init swap on zram-based device zram0
   Loaded: loaded (/nix/store/vrfmjfr3qg2sq75dc1v6amha3xpba37g-unit-zram-init-zram0.service/zram-init-zram0.service; enabled>
   Active: active (exited) since Thu 2020-04-16 20:25:56 UTC; 1s ago
  Process: 2156 ExecStart=/nix/store/b8x52yy9c5q4zrvhflx1wl6mqpr3ivv0-unit-script-zram-init-zram0-start (code=exited, status>
 Main PID: 2156 (code=exited, status=0/SUCCESS)
       IP: 0B in, 0B out
      CPU: 25ms

Apr 16 20:25:56 angel systemd[1]: Starting Init swap on zram-based device zram0...
Apr 16 20:25:56 angel b8x52yy9c5q4zrvhflx1wl6mqpr3ivv0-unit-script-zram-init-zram0-start[2156]: Setting up swapspace version>
Apr 16 20:25:56 angel b8x52yy9c5q4zrvhflx1wl6mqpr3ivv0-unit-script-zram-init-zram0-start[2156]: no label, UUID=441a72a6-d812>
Apr 16 20:25:56 angel systemd[1]: Started Init swap on zram-based device zram0.

I don’t know whether zram-init-zram0 is the service that’s actually supposed to add the swap space, but in any case swapon -s still doesn’t show any zram. Finally, if I manually run swapon -p 5 /dev/zram0 it does appear in the output of swapon -s.

Any idea where to look next? By default I guess my next step would either be be to see if I can reproduce the problem with nixos-rebuild build-vm, or to learn more about how the NixOS zram module works so I actually know what should be happening.

Here’s my configuration.nix:

{ config, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];

  boot = {
    initrd = {
      luks.devices = {
        # nixos-generate-config puts SfricBtrSec into
        # hardware-configuration.nix but not KFricBtrSec. I don't know why.
        KFricBtrSec = { device = "/dev/disk/by-uuid/dab9a0f9-c45d-4e4f-8157-18c67518f955"; };
      };
      supportedFilesystems = [ "btrfs" ];
    };
    loader.grub = {
      enable = true;
      version = 2;
      device = "/dev/sdb";
    };
  };

  environment.systemPackages = [ pkgs.manpages ];

  fileSystems =
    let btrfsOptions = [ "autodefrag" "noatime" ]; in
    {
      "/".options = btrfsOptions;
      "/btr".options = btrfsOptions;
      "/home".options = btrfsOptions;
      # boot.tmpOnTmpfs doesn't allow the size to be configured.
      "/tmp" = {
        fsType = "tmpfs";
        device = "tmpfs";
        # Options based on output of mount | grep /tmp with boot.tmpOnTmpfs
        # option --- I'm trying to mimic that. size option added.
        options = [ "nosuid" "nodev" "size=32G" ];
      };
    };

  networking = {
    extraHosts = ''
      10.167.0.1 sylph
      fc00:f69a:9457:2800::1 sylph
      10.167.0.3 copter
      fc00:f69a:9457:2800::3 copter
      10.167.0.4 angel
      fc00:f69a:9457:2800::4 angel
      10.167.0.6 moth
      fc00:f69a:9457:2800::6 moth
    '';
    
    hostName = "angel";

    interfaces."tinc.falsifian" = {
      ipv4.addresses = [ { address = "10.167.0.4"; prefixLength = 16; } ];
      ipv6.addresses = [ { address = "fc00:f69a:9457:2800:0:0:0:4"; prefixLength = 64; } ];
    };
    
    # The global useDHCP flag is deprecated, therefore explicitly set to false here.
    # Per-interface useDHCP will be mandatory in the future.
    useDHCP = false;
    interfaces.enp3s0.useDHCP = true;
    interfaces."tinc.falsifian".useDHCP = false;
    interfaces.wlp4s6.useDHCP = false;
  };

  nix = {
    daemonIONiceLevel = 5;
    daemonNiceLevel = 10;
    gc = {
      # This slows down the system too much (as of 2020-03-28). Maybe if it
      # were changed to use nice it would be okay.
      automatic = false;
    };
  };


  i18n = {
    defaultLocale = "en_CA.UTF-8";
  };

  time.timeZone = "Etc/UTC";

  security.sudo.wheelNeedsPassword = false;

  services = {
    openssh.enable = true;

    printing = {
      drivers = [ pkgs.gutenprint ];
      enable = true;
    };

    tinc.networks.falsifian = {
      extraConfig = "ConnectTo = sylph";
      hosts = {
        angel = ''
          Port = 43128
          Subnet = 10.167.0.4
          Subnet = fc00:f69a:9457:2800:0:0:0:4
        '';
        moth = ''
          Subnet = 10.167.0.6
          Subnet = fc00:f69a:9457:2800:0:0:0:6
          Ed25519PublicKey = Os6RhCSPs+PlTr8wq9VUpU0PPgTYszsZRik3k+EKUOC
          -----BEGIN RSA PUBLIC KEY-----
          MIICCgKCAgEAvxXBsK3Mg7r76N5WfBbW678qvgZZhmauiHQLBi3WOyAegbjsGq9O
          R/iB9hSreolqFa3XC5N0wcOzmi6N/ECt2BLF7FapZekeVhVB0ayIBEJ4PQkCrn30
          AAJdDhFozPPezmSftCH9yw4HXk1YOcALxux3Lvp87JjDv6C+Js0tVeXAUD870LDR
          FyPYSuS4cK1/4uVfX5SxWh1XCLFPi6ZUdbpPhyVr9l+rkKa6Tz1gd4m6BmpwFG3Z
          aBT0Nba2Zr8Y25/T/l3O+9MmOgtLc9yqYzsgD+eh5a4Q6e1lXOY2onIaKwWuABmo
          mjQacZrcjBGBBaVKwIDFhU+p16/1z0fmnSH7Yvj+ztKskfD09iRAe1hNzuk0hS3K
          B5F3Bgsn7ngvSQTRat+1wbGmp9t6s3QBUWUh+aVXrtIu4pEJdagR+G0yRcKZhz1e
          ybHluB9JiCqjolociZTWrXTmfsTOJP2oDsdE9HMD4nkp+npCOvep0ahYrTBiZEEA
          YWuEx34bM3C9+EWkkR83Hi25cQTnyJhAmue084+JOSmIFw5x1jZ+1XyXur1nCbn4
          LXno+vwbwm5WkPGyNWfw7/iTEmA87j5u8OYNt7jZFKCKEUBBXm6Q2ZIPsfA4kCao
          QQA/vm18Q/xWzT5IT3kuWdj5/MCdHSVIMfnqY6XSBUDe4XyCCssl4nUCAwEAAQ==
          -----END RSA PUBLIC KEY-----
        '';
        sylph = ''
          Address = 50.16.203.136
          Subnet = 10.167.0.1
          Subnet = fc00:f69a:9457:2800:0:0:0:1

          -----BEGIN RSA PUBLIC KEY-----
          MIIBCgKCAQEAv17ZOV632hTtJR4kLjE3i3bNr4HLMh+9Ohm7mrr2mF2tb07XOKuo
          S3DwtmfbzgnnBIuVXhhdP2j22yDBPBIq+MsBXGag+xZWjbO4a9CV6weqZ2DYGaPL
          X4tYeQds6LpiKR/+aXmbGYdvc9AYyECnfY0jXM1XS2I8PA8SPl5KVc3Yxrlcz0lv
          /4ph85HmSizQObamb0rxm3ifYRZ4n6V3zeGXYnzREVMWN2Fko4ke7J5TBBGODApj
          Rldav10jNuUBY5s8ffitbMBEsQ4LAXgL9bvkKBnG0d+fY4TdvaGVxqh3ppB+nEkW
          1vPRvW7A1BUxXQoaGUNM/AGJjsqkP9jj4wIDAQAB
          -----END RSA PUBLIC KEY-----
        '';
      };
      name = "angel";
    };

    # Use bfq scheduler since it respects ionice.
    #
    # udev rule based on https://releases.nixos.org/nix-dev/2016-November/022137.html
    udev.extraRules = ''ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="bfq"'';

    xserver = {
      desktopManager.plasma5.enable = true;
      displayManager.sddm.enable = true;
      enable = true;
    };
  };

  # nixos-generate-config puts /dev/disk/by-uuid/something into swapDevices,
  # which is unhelpful because I want the randomEncryption option. I think the
  # uuid will exist anyway since I think it will be different on every boot. So
  # use pkgs.lib.mkForce to override it instead of merging this list with it.
  swapDevices = pkgs.lib.mkForce [
    {
      device = "/dev/disk/by-partuuid/1a2a682c-5001-4088-922e-70819ed67f37";
      priority = 0;
      randomEncryption.enable = true;
    }
    {
      device = "/dev/disk/by-partuuid/5bbdff04-7dbe-4948-ac88-0797b60b9971";
      priority = 0;
      randomEncryption.enable = true;
    }
  ];

  users.users = {
    colin =
    { createHome = true;
      home = "/home/colin";
      isNormalUser = true;
    };
    james = {
      isNormalUser = true;
      extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
    };
  };

  zramSwap = {
    enable = true;
    algorithm = "zstd";
  };

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "20.03"; # Did you read the comment?

}

and hardware-configuration.nix:

# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

{
  imports =
    [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
    ];

  boot.initrd.availableKernelModules = [ "ahci" "ohci_pci" "ehci_pci" "pata_atiixp" "pata_jmicron" "firewire_ohci" "usbhid" "sd_mod" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ "kvm-amd" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/mapper/SFricBtrSec";
      fsType = "btrfs";
      options = [ "subvol=nixos_root" ];
    };

  boot.initrd.luks.devices."SFricBtrSec".device = "/dev/disk/by-uuid/cc82b84c-b312-44cf-a2cf-2e5e9c5a8d20";

  fileSystems."/btr" =
    { device = "/dev/mapper/SFricBtrSec";
      fsType = "btrfs";
    };

  fileSystems."/home" =
    { device = "/dev/mapper/SFricBtrSec";
      fsType = "btrfs";
      options = [ "subvol=home" ];
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/4a621c7a-c1f9-4ce1-a0e5-8acdde829dae";
      fsType = "ext2";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/f709186b-5015-47d5-a229-2e1e29546f6a"; }
    ];

  nix.maxJobs = lib.mkDefault 3;
  # High-DPI console
  console.font = lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-u28n.psf.gz";
}

I have the same configuration and i can confirm that it just works.

[ich@nixos-schenker:~]$ swapon -s
Filename                                Type            Size    Used    Priority
/dev/sda3                               partition       6009852 1462952 -2
/dev/sda6                               partition       19121148        0       -3
/dev/zram0                              partition       2006392 2006256 5

But i think i recall that the starting of the service after rebuild acts not as with others (as makes sense, because there needs to be your RAM half empty)
Have you tried turning it off and on again?

Yes, the symptoms I described are after reboot.

Side note: unless I’m misunderstanding something, I don’t think zram would actually need half my ram available at the moment I activate it, even with the default zramSwap.memoryPercent of 50.

For example, right now about 11GB of my 16GB of RAM is used by buffers/cache, from which I infer that zram has not reserved half my memory. I thought the zram device will only push other stuff out of the way when it’s actually needed. But my understanding of this stuff is only cursory.

james angel ~ $ zramctl
NAME       ALGORITHM DISKSIZE  DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 zstd          7.8G  401M 72.6M 83.1M       3 [SWAP]
james angel ~ $ free -m
              total        used        free      shared  buff/cache   available
Mem:          16018        1808        2771         128       11438       13751
Swap:         73545         403       73141
james angel ~ $ 

I found that it is reproducible with nixos-rebuild build-vm, and filed a bug: zramSwap.enable doesn't work with nixos-rebuild build-vm · Issue #86061 · NixOS/nixpkgs · GitHub

I’m mystified because my configuration seems the minimum possible, and still zramSwap.enable = true does nothing. If it’s working for other(s), I wonder if there’s something basic I’m misunderstanding.