Mixing stable and unstable packages on flake-based NixOS system

Hello everyone. My system is based on flakes (nix-channel is disabled). How can I use some packages from nixos-unstable while keeping the rest of my system on the stable branch? I’d like to achieve something like this:

environment.systemPackages = with pkgs; [
    firefox
    thunderbird
    unstable.osu-lazer-bin
];

In other words, I wanna be able to access osu-lazer-bin from the unstable branch via pkgs.unstable.osu-lazer-bin.
I’ve already seen multiple approaches to achieve this, but none of them worked for me, maybe it’s because my system uses flakes, idk.

translated with llama 3.1 405b :3

You should be able to add the unstable nixpkgs as part of your inputs, then import it and add that package directly. Something like this:

 inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/release-24.05";
    nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
 };

 outputs = {nixpkgs, nixpkgs-unstable, ... }:
 let
   system = "x86_64-linux";
   pkgs = import nixpkgs { inherit system; };
   pkgsUnstable = import nixpkgs-unstable { inherit system; };
 in
 {
    # now along with `pkgs` you also have `pkgsUnstable`
    # and can do: pkgsUnstable.osu-lazer-bin
 }

Something like this should work:

  overlay-unstable = final: prev: {
    unstable = import nixpkgs-unstable {
      inherit system;
      config.allowUnfree = true;
    };
  };

  pkgs = import nixpkgs {
    inherit system;
    config = {
      allowUnfree = true;
    };
    overlays = [
      overlay-unstable
    ];
  };

Then you can do pkgs.firefox or pkgs.unstable.firefox.

2 Likes

Never set pkgs directly in the NixOS module system, as the other answers are indicating.

Here’s a couple of concepts you need to know:

  1. If you want to access some inputs, or some other value across your config without creating an explicit option for it, use specialArgs or _module.args. (For things that you import, you must pass them in specialArgs specifically to avoid infrec.)
  2. Changing pkgs requires an overlay; however, for this application, you may not necessarily need one if you are willing to get away from the pkgs.unstable syntax specifically.

Which means the following:

  1. Add an entry to your inputs, and pass them to your config via specialArgs, in your flake.nix:

    inputs = {
      nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
      nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
    };
    
    outputs = { nixpkgs, ... }@inputs:
      nixosConfigurations = {
        foo = nixpkgs.lib.nixosSystem {
          #...
          specialArgs = { inherit inputs; };
        };
      };
    };
    
  2. Since you need an unfree package specifically, allow the unfree package somewhere in your NixOS config:

    {
      nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
        "osu-lazer-bin"
      ];
    }
    

    or if you want, you can just blanket allow all unfree packages, that’s up to you:

    {
      nixpkgs.config.allowUnfree = true;
    }
    
  3. Then, use that config when importing the unstable nixpkgs, and then use the appropriate module arg in your package list.
    a. EITHER create a new module arg:

    { config, inputs, pkgs, pkgsUnstable, ... }:
    {
      # this allows you to access `pkgsUnstable` anywhere in your config
      _module.args.pkgsUnstable = import inputs.nixpkgs-unstable {
        inherit (pkgs.stdenv.hostPlatform) system;
        inherit (config.nixpkgs) config;
      };
    
      environment.systemPackages = [
        pkgsUnstable.osu-lazer-bin
      ];
    }
    

    b. OR, add an overlay to modify the existing nixpkgs instance (pkgs):

    { inputs, pkgs, ... }:
    {
      nixpkgs.overlays = [
        (final: _: {
          # this allows you to access `pkgs.unstable` anywhere in your config
          unstable = import inputs.nixpkgs-unstable {
            inherit (final.stdenv.hostPlatform) system;
            inherit (final) config;
          };
        })
      ];
    
      environment.systemPackages = [
        pkgs.unstable.osu-lazer-bin
      ];
    }
    
    

I’m personally against 3b, because an overlay for this is completely unnecessary and will likely slow down eval.

PS this has got to be one of the most-asked questions, it’s silly that we are still having to rewrite the answers from scratch in 2024…

4 Likes

Is that what is happening here?

You could add an article to nix.dev or the wiki.

Perfect! Used option 3a, this should definitely be written somewhere in the docs, at least not official.

Hello, I am a bit new to flakes and the nix syntax. The tutorial i followed led me to a different outputs syntax so i am not sure how i can follow your step 1. Can you help? I am figuring this stuff out as i go so there are some holes in my understanding.

{
  description = "My system configuration";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs, ... }:
    let
      lib = nixpkgs.lib;
    in {
      nixosConfigurations = {
        tartarus = lib.nixosSystem {
          system = "x86_64-linux";
          modules = [ ./configuration.nix ];
          # revision = self.shortRev or self.dirtyShortRev or self.lastModified or "unknown";
        };
      };
    };
}