How to write a universal module that can work on darwin (using nix-darwin) & NixOS

I have a module that basically looks like this.

{ pkgs, lib, config, ... }:

with config.settings;

let

  cfg = config.my.foo;

in {
  options = with lib; {
    my.foo = {
      enable = mkEnableOption ''
        Whether to enable foo module
      '';
    };
  };

  config = with lib;
    mkIf cfg.enable (mkMerge [
      (mkIf pkgs.stdenv.isDarwin {
        launchd.user.agents."foo" = {
        # some config
        };
      })
      (mkIf pkgs.stdenv.isLinux {
        # nixos config
      })
    ]);
}

The problem is when I run my setup on NixOS I get the following error

The option 'lanuchd' does not exist.

I thought that it would be enough to check the platform, but looks like this is not enough?

Might be wrong here, but modules don’t run on darwin, because you need full NIX/OS for that to work?

darwin just does the pkgs stuff from nixpkgs, not the modules.?

I am not sure if a way exists currently, though I am planning on solving this as part of my devos project, just haven’t nailed down a design spec just yet. I want to be able to transpose modules, between nixos, home-manager, nix-darwin and possibly devshell.

They may not be entirely equivalent, since devshell, for example, doesn’t support services, but I’m thinking what I’ll end up having to do is declare a generic module that then gets converted to the specified platform.

Another alternative I’ve considered is to start an initiative in each project to synchronize module API so we can easily pass them around. This may cause a lot of headaches though.

Thankfully, there is some overlap between NixOS and nix-darwin modules. So some of them may work just fine as is.

nix-darwin exists for the use of modules to configure your darwin system.

all you need know is to change the boot-loader in the mac UEFI to boot a linux kernel, and you’ll amost be at Nix/OS :wink:

good to know.

ping @7c6f434c who had a very interesting discussion about cross platform (bsd, for example) NixOS modules with @sander during the last nixcon.

1 Like

Is this discussion posted anywhere?

This is what I ended up doing & it’s working great.

{ pkgs, lib, config, ... }:

let

  cfg = config.my.foo;

in {
  options = with lib; {
    my.foo = {
      enable = mkEnableOption ''
        Whether to enable foo module
      '';
    };
  };

  config = with lib;
    mkIf cfg.enable (mkMerge [
      (if (builtins.hasAttr "launchd" options) then {
        launchd.user.agents."foo" = {
          # some config
        };
      } else {
        # systemd
      })

      {
        # shared stuff goes here
      }
    ]);
}

That’s correct, so I’m currently working on the Nix process management framework: GitHub - svanderburg/nix-processmgmt: Experimental Nix-based process management framework

The framework has a variety of purposes – one of them is targeting multiple process managers from the same declarative specification (e.g. sysvinit, systemd, launchd etc.) and it should work on other operating systems than Linux, including macOS and FreeBSD.

It also has some other objectives, such as the ability to construct multiple instances of the same proces configuration and a switch to disable user authentication so that an unprivileged Nix user can deploy services.

Although it is already somewhat usable, it is currently still an unfinished prototype. There are two more major features on the planning.