WHy would the location of network.networkmanager make a difference?

Hi there.

I have my repo structured so that all my modules are defined, and then I enable them in my system configs as needed. For the shared modules that exist on all of my workstations, I have an “archetype” module called “workstation” that includes the shared config/modules and ones called “tower” and “laptop” for the HW-specific config (IE wifi, or other things).

So in my case for example for my laptop I have:

{ config, pkgs, lib, ... }:
let cfg = config.archetype.laptop;
in {

  options = {
    archetype.laptop.enable = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Enable the laptop archetype.";
    };
  };

  config = lib.mkIf cfg.enable {

    hw = {
      fwupd.enable = true;
      audio.enable = true;
      touchpad.enable = true;
      fprintd.enable = true;
      networking = {
        networkmanager.enable = true;
        wifi.powersave = true;
      };
      bluetooth = {
        enable = true;
        logitech.solaar = true;
        airpods.join = false;
      };
    };

  };
}

And in my example we can see where networking was actually implemented over in my hw.networking module:

{ pkgs, config, lib, ... }:
let cfg = config.hw.networking;
in {

  options = {
    hw.networking.networkmanager.enable = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Enable networkmanager.";
    };
    hw.networking.wifi.powersave = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Enable wifi powersaving.";
    };
  };

  config = lib.mkIf cfg.networkmanager.enable {
    # Enable networking
    networking.networkmanager.enable = true;
  } // lib.mkIf cfg.wifi.powersave {
    # Enable wifi powersaving
    environment.systemPackages = [

    ];

    # wifi powersaving
    boot.extraModprobeConfig = ''
      options iwlwifi power_save=1
    '';
  };
}

Nothing crazy here. All works great in Gnome.

Now for some reason as I am building out a sway config, the networking.networkmanager.enable = true; does not seem to work. IE the tools are missing, there is no services for NetworkManager, etc. Like if is not configured at all.

If I add networking.networkmanager.enable = true; in my sway module, it works.

I can’t grasp:

  1. why it would work in Gnome, but now Sway
  2. why the “location” of the code (as described) would matter.

Any ideas?

Thank you.

REFERENCES

Chances are you’re just not importing the module that enables networkmanager when you’ve set up sway, or you manage to make the cfg.enable false somehow. NixOS merges all module attrsets together.

Maybe you have some setting in the gnome module that makes the laptop/desktop ones work that you’re overlooking?

You cannot join mkIfs with //, the function returns an attribute set of specific shape, so the attributes of the second mkIf will shadow the attributes of the first one. You need to use mkMerge.

And yes, the GNOME module enables NetworkManager out of the box.

2 Likes

I am, the way I have it structured, I enable the desktop of choice with desktops.gnome.enable = true; or desktops.sway.enable = true; . The part with NetworkManager was enabled with archetype.laptop = true; - which was never changed when toggling desktops.

I will double-check, though.

Oh, nice catch! This was in the early days when I was learning about adding options. I just never caught it, as there were no rebuild errors, and the network was working everywhere. I have since learnt about mkMerge and have been using it elsewhere.

It makes me wonder if there is a better way to catch items like this when there is no feedback and you “think” it is working.

Thanks!

That’s basically what linters are for. I don’t think statix has one specifically for this scenario, but maybe one could be added?

Yeah, I will need to look into that. I mostly relied on Nixfmt without realizing it was just formatting without linting. Just added Statix to my install and will add to my justfile.

Also to close out the original issue, my hw.networking module ended up at:

{ pkgs, config, lib, ... }:
let cfg = config.hw.networking;
in {
  options.hw.networking = {
    networkmanager = {
      enable = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = "Enable networkmanager.";
      };
    };
    wifi = {
      powersave = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = "Enable wifi powersaving.";
      };
    };
  };

  config = lib.mkMerge [
    (lib.mkIf cfg.networkmanager.enable {
      # Enable networking
      networking.networkmanager.enable = true;
    })
    (lib.mkIf (cfg.networkmanager.enable && cfg.wifi.powersave) {
      # Apply wifi powersaving configuration only if both networkmanager is enabled and wifi powersave is true
      boot.extraModprobeConfig = ''
        options iwlwifi power_save=1
      '';
    })
  ];
}

I didn’t need to use mkMerge since I also have the boot.extraModprobeConfig improperly nested.

But the mkIfs with // pointed out by @jtojnar was key.

Thank you all!