You don't need nixpkgs overlays

Okay yes of course there are some use cases for them, we don't have them for no reason! but you almost certainly do not need them. 90% of the times I've seen people use an overlay there is another simpler, easier to read, faster way to do what they want to do. Overlays are often one of the hardest concepts for newcomers to nix to understand, and for good reason! They're not easy to grok on first read. That's why I think it's especially important that we teach new comers that they likely do not need overlays and frankly as a community we're currently doing more or less the opposite: Telling newbies to use overlays even in places they really do not need them and growing a community in general that uses them where they really shouldn't be used. So what do people mostly use overlays for and why do they not need them for that?

Non-nixpkgs packages

Probably the most common use of overlays is to add packages to your system that don't come from nixpkgs. This is also the use case that most clearly does not need overlays. If you want to use a package that's not in nixpkgs ... just do that! you don't need to merge it into your instance of nixpkgs. I promise you there is no reason to do that.

Let's look at a fictionalised example to see why and how to actually do it instead. A pretty common thing to do in the nix ecosystem is to find some flake that offers a set of packages that you want to use. This often ends up looking somewhat like below when using overlays:

# flake.nix
{
inptus = {
nixpkgs = {
url = "github:nixos/nixpkgs/nixos-unstable";
};
abpkgs = {
url = "github:aborg/abpkgs/release-0.6";
};
};

outputs = {
nixpkgs,
...
} @ inputs: {
nixosConfiguations."my-desktop" = nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; };
modules = [ ./configuration.nix ];
};
};
}

# configuration.nix
{ inputs, pkgs, ... }: {
nixpkgs.overlays = [ inputs.abpkgs.overlays.default ];

environment.systemPackages = [ pkgs.abapp ];
}

if you've been in the nix ecosystem long enough you've almost certainly written something like the above. Now what's the problem with this? Well for one for the untrained (newbie) eye it's not immediately easy to see where abapp is actually coming from. It's not in nixpkgs so why is it in the nixpkgs package set? From an optimisation perspective this is also slow. Adding an overlay means increasing how much time is spent evaluating nixpkgs. Every overlay is effectively another recompute of the nixpkgs fixed point. This isn't tremendous with a single overlay but if you have 3, 4, 5 then it starts to add up. Likewise with how much RAM it takes to evaluate your system config. So what should you do instead? The flake.nix can stay the same but instead of pulling in and applying the overlay you can just take the package from the flake outputs of abpkgs instead!

# configuration.nix
{ inputs, pkgs, ... }: {
environment.systemPackages = [
inputs.abpkgs.${pkgs.stdenv.hostPlatform.system}.abapp
];
}

Now ... I know that pkgs.stdenv.hostPlatform.system is a little ugly but that's there to be generic over your system. If you want to commit sins and use x86_64-linux instead and have to remember to change it if you ever use that module for an aarch64-linux build then be my guest. That's not the point. The point is we got the exact same result in a way where: It's easier to see where the package is from, eval is faster and uses less RAM, and you don't needlessly indirect through your instance of nixpkgs0. Now of course you're relying here on the flake having package outputs but for one: if they don't, then that's a bad flake and I'd question if it's worth using in the first place and two you're also relying on a flake having an overlay output when you use the overlay method. Now in some cases you have some specific instance of nixpkgs and you want to get an instance of abpkgs that uses that nixpkgs as it's base. You might go for overlays here but if abpkgs is well made this is where it will let you build your own instance of it's package set so you should probably instead do something like this:

# configuration.nix
{ inputs, pkgs, ... }: let
abpkgs = import inputs.abpkgs pkgs;
in {
environment.systemPackages = [
abpkgs.abapp
];
}

This one depends even more on what your flake input let's you do. There is no standardised way to expose this feature. The code above assumes there's a default.nix in the flake root that build the package set when given an instance of nixpkgs. Some flakes instead expose this as a function under the lib output of the flake, some are bad and don't even expose this functionality. Adjust as needed.

Changing a package in nixpkgs on your system

So importing non-nixpkgs packages is the most egregious issue but the people have more! What if you want to make a nixos system that runs say ... a very specific version of nginx? Then surely an overlay is good. We want to change a package in nixpkgs afterall. Doing that would look something like:

# configuration.nix
{ pkgs, ... }: {
nixpkgs.overlays = [(final: prev: {
nginx = prev.nginx.override { withPerl = false; };
})];

services.nginx = {
enable = true;

< more config >
};
}

But no you most likely don't want an overlay here either. You get basically all the same issues as mentioned above: slower eval, more RAM usage in eval and the overlay and the place where it matters are less connected0. The module system has a better solution. Every good service module will have a package option. This is often used to change what package is used when nixpkgs has multiple versions of the same software packaged, but nothing says you can't set it to your own version of that software. Let's make one with an override for simplicity0:

# configuration.nix
{ pkgs, ... }: {
services.nginx = {
enable = true;
package = pkgs.nginx.override { withPerl = false; };

< more config >
};
}

That's a heck of a lot cleaner wouldn't you say? I don't think I need to harp on about this one too much.

So ... where should you use overlays? Part of me wants to say that if you're asking if you should use them then you probably shouldn't. But in general: you only want to use overlays if you want to change a package in your nixpkgs instance that other packages depend on and you want all those other packages to depend on your modified version. I struggle to think of a proper use case for overlays that doesn't just boil down to that.

If you enjoyed this you'll probably also enjoy " 1000 instances of nixpkgs " which makes largely the same argument but for some other use cases you may or may not care about. Check also " I'm not mad, I'm disappointed " which is a more general dos and don'ts of using nix. So uh yea ... STOP TELLING PEOPLE TO USE OVELAYS FOR SHIT please I beg you.