Using Both Stable and Unstable Packages on NixOS (At the Same Time!)
Associated Youtube Video: https://youtu.be/hlytf6Uxf4E
Associated Odysee Video:
Previous Tutorial: Program a Modular Control Center for Your Config Using Special Args in NixOS Flakes
Next Tutorial: Managing Your NixOS Config with Git
Have you ever wanted to dual wield different releases/branches of your packages? Well now you can, thanks to the power of Nix!
Table of Contents
Disclaimer
This is a bit of a hacky solution which will essentially allow you to install different packages from different versions of nixpkgs simultaneously. This should mainly be used as a last resort to get things to work if they don’t work normally. Although Nix is basically magic, and this should™ work, there’s always the possibility of weird and undefined behavior resulting from tossing different versions around all at once.
Tools Needed
- A system with the Nix Package Manager installed
- Your configuration managed by a
flake.nix
- Home-manager installed with
home.nix
(optional) specialArgs
(andextraSpecialArgs
if you use home-manager) setup in your flake
Setting Up Two (or more) Different Nixpkgs in Your Flake
In order to setup more than two versions of nixpkgs (i.e. stable and unstable), we’re going to utilize the specialArgs
(and/or extraSpecialArgs
) feature of the lib.nixosSystem
(and/or the home-manager.lib.homeManagerConfiguration
) function(s). My last blog post had a discussion about how to utilize those but the tldr is that you can add arbritrary arguments that get passed to every Nix module file, i.e the following example, which passes the username
and name
variables as extra arguments to the Nix modules:
{ description = "My first flake!"; inputs = { nixpkgs.url = "nixpkgs/nixos-23.05"; home-manager.url = "github:nix-community/home-manager/release-23.05"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, home-manager, ... }: let system = "x86_64-linux"; lib = nixpkgs.lib; pkgs = nixpkgs.legacyPackages.${system}; username = "librephoenix"; name = "Emmet"; in { nixosConfigurations = { YOURHOSTNAME = lib.nixosSystem { inherit system; modules = [ ./configuration.nix ]; specialArgs = { inherit username; inherit name; }; }; }; homeConfigurations = { USERNAME = home-manager.lib.homeManagerConfiguration { inherit pkgs; modules = [ ./home.nix ]; extraSpecialArgs = { inherit username; inherit name; }; }; }; }; }
specialArgs
are used by the nixosSystem
function, whereas extraSpecialArgs
are used by the homeManagerConfiguration
function from home-manager.
In the last post, we utilized this simply to create reusable variables across every Nix module file in the configuration, but here we’re going to take this a step further.
You can see that pkgs
is defined as nixpkgs.legacyPackages.${system}
, where the nixpkgs
parts comes from the inputs. Using this idea, you can add as many different arbitrary versions of nixpkgs to your inputs, and add multiple definitions of pkgs
, like so:
{ description = "My first flake!"; inputs = { nixpkgs.url = "nixpkgs/nixos-23.05"; nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; home-manager.url = "github:nix-community/home-manager/release-23.05"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, ... }: let system = "x86_64-linux"; lib = nixpkgs.lib; pkgs = nixpkgs.legacyPackages.${system}; pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; username = "librephoenix"; name = "Emmet"; in { nixosConfigurations = { YOURHOSTNAME = lib.nixosSystem { inherit system; modules = [ ./configuration.nix ]; specialArgs = { inherit username; inherit name; inherit pkgs-unstable; }; }; }; homeConfigurations = { USERNAME = home-manager.lib.homeManagerConfiguration { inherit pkgs; modules = [ ./home.nix ]; extraSpecialArgs = { inherit username; inherit name; inherit pkgs-unstable; }; }; }; }; }
In the above example, you can see that we have definitions for both pkgs
and pkgs-unstable
. If these are added to specialArgs
and/or extraSpecialArgs
, you can now utilize them in your configuration.nix
, home.nix
, or any other Nix module. Here’s a trivial example showing how you would install both stable and unstable packages installed in the same module:
{ config, lib, pkgs, pkgs-unstable, ... }: { environment.systemPackages = (with pkgs; [ # list of stable packages go here ]) ++ (with pkgs-unstable; [ # list of unstable packages go here ]); }
Some Caveats
You Can’t Install Two Versions of the Same Package
The following example obviously doesn’t work, unless the package definition is identical between the two versions of nixpkgs in question:
{ config, lib, pkgs, pkgs-unstable, ... }: { ## TWO VERSIONS OF SAME PACKAGE (BINARY) DOESN'T WORK!! environment.systemPackages = (with pkgs; [ vim emacs ]) ++ (with pkgs-unstable; [ vim emacs ]); }
Keep in Mind What is Actually Happening!
Going back to the example from above again:
{ description = "My first flake!"; inputs = { nixpkgs.url = "nixpkgs/nixos-23.05"; # <-- STABLE nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; # <-- UNSTABLE home-manager.url = "github:nix-community/home-manager/release-23.05"; # <-- STABLE home-manager.inputs.nixpkgs.follows = "nixpkgs"; # <-- STABLE }; outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, ... }: let system = "x86_64-linux"; lib = nixpkgs.lib; pkgs = nixpkgs.legacyPackages.${system}; pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; username = "librephoenix"; name = "Emmet"; in { nixosConfigurations = { YOURHOSTNAME = lib.nixosSystem { # NIXOS SYSTEM BUILDER FUNCTION FROM STABLE # though pkgs isn't directly inherited here, like with home manager # pkgs <-- STABLE inherit system; modules = [ ./configuration.nix ]; specialArgs = { inherit username; inherit name; inherit pkgs-unstable; # <-- UNSTABLE }; }; }; homeConfigurations = { USERNAME = home-manager.lib.homeManagerConfiguration { # <-- HOME MANAGER BUILDER FUNCTION FROM STABLE inherit pkgs; # <-- STABLE modules = [ ./home.nix ]; extraSpecialArgs = { inherit username; inherit name; inherit pkgs-unstable; # <-- UNSTABLE }; }; }; }; }
note the comments to the side of the nixosSystem
function, homeManagerConfiguration
function and pkgs
/ pkgs-unstable
inherits. It’s easy to see that, even though we’re including two differents versions of nixpkgs, we can only choose one nixpkgs version to actually build the system (or home manager configuration).
I’d like to think that since Nix is magic, it probably has the capacity to always work out in the end. However, it’s more likely the case that there could still be some weird behavior leading to breakages, especially if something is deprecated or added in the definitions for the nixosSystem
function (or homeManagerConfiguration
function).
The End
I hope you found this helpful! I’ve found this trick especially useful whenever some packages I really need fail to build (since I actually run the unstable branch of nixpkgs myself). This also should allow easy access to packages that are currently only in the unstable branch, if you’d like to keep the rest of your system on stable.
In any case, happy configuring! ;)
Donation Links
If you have found my work to be helpful, please consider donating via one of the following links. Thank you very much!