____ _ | __ )| | ___ __ _ | _ \| |/ _ \ / _` | | |_) | | (_) | (_| | |____/|_|\___/ \__, | |___/_ _ _/_| |__ ___ _ __| |_ _ _/_ / _ \ '_ \ / _ \ '__| | | | |/ _ \ __/ |_) | __/ | | | |_| | __/ \___|_.__/ \___|_| |_|\__,_|\___|
[2025-03-08][Nix]
In this tutorial I will show you how to chose a specific version of a package using overlays. On my config I needed to use the last version of dwl window manager because all my patches were using the main branch of the repo. However, as of writing this, only the version 0.7 is available in nixpkgs. We can get this information from search.nixos.org/packages, but make sure to select correctly your nixpkgs branch.
By the way, you can check your nix channel with the following command:
andrew@nixos ~> sudo nix-channel --list
nixos https://nixos.org/channels/nixos-unstable
The one starting with "nixos" should indicate the version you are using. ⚠️ Omitting sudo will give you a different list, it seems like nix-channel can manage channels per users, so not using sudo will give you the channels for your user and not for the system ⚠️
Anyways, one way you could change the version of the package would be to copy the entire 📦source, change the branch that is used, and put the file in our config.
We will see that overlays are a better way to do this, but to begin with, we need to understand how **derivations** works. A derivation is like a recipe, it gives you the steps to ~cook~ build an application. Then this recipe can be evaluated by nix to actually build (and install) this application. Let's break the *dwl*'s derivation into multiple parts and explain it:
{
lib,
fetchFromGitea,
installShellFiles,
libX11,
libinput,
libxcb,
libxkbcommon,
pixman,
pkg-config,
stdenv,
testers,
wayland,
wayland-protocols,
wayland-scanner,
wlroots,
writeText,
xcbutilwm,
xwayland,
# Boolean flags
enableXWayland ? true,
withCustomConfigH ? (configH != null),
# Configurable options
configH ?
if conf != null then
lib.warn ''
conf parameter is deprecated;
use configH instead
'' conf
else
null,
# Deprecated options
# Remove them before next version of either Nixpkgs or dwl itself
conf ? null,
}:
This part can be viewed as the arguments of the derivation (or the list of ingredients), it
specifies the dependencies of our application (libX11, libinput, etc.), but is also able to take
user defined arguments such as enableXWayland that defaults to true thanks to this
notation ? true.
nix
stdenv.mkDerivation (finalAttrs: {
pname = "dwl";
version = "0.7";
src = fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = "v${finalAttrs.version}";
hash = "sha256-7SoCITrbMrlfL4Z4hVyPpjB9RrrjLXHP9C5t1DVXBBA=";
};
...
)
Here comes the part where we actually start writing our recipe. Notice that the whole derivation
is wrapped into stdenv.mkDerivation which is actually a function to create derivations,
you may take a look at this amazing
documentation to learn more. In our case, this part is what we would like to modify: the
version. variable is at 0.7 just as we saw on nixpkgs. However changing only this
variable wouldn't work because the hash in the function fetchFromGitea is only valid
for the branch 0.7. So we need to change to hash aswell, to do so we can leave it empty and nix will
give it to us:
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
...
error: hash mismatch in fixed-output derivation '/nix/store/gxrbli79q4wlpa33q1vhsvri4s10kwgz-source.drv':
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=
Now you can copy the hash, and we should obtain something like this:
stdenv.mkDerivation (finalAttrs: {
pname = "dwl";
version = "main";
src = fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = finalAttrs.version;
hash = "sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=";
};
...
)
Here I specify the main version because this is the branch that I want. Notice that rev is
actually the parameter that controls which branch is used, and it is getting the
version value using finalAttrs.version. So as said previously, you could
copy the whole source and put it in a local file accessible to your config:
# configuration.nix
{ ... }:
let
dwl-custom = pkgs.callPackage ./dwl/default.nix { configH = ./dwl-config.h; };
in
{
environment.systemPackages = with pkgs; [
dwl-custom
]
# Rest of the config...
}
However, when the derivation will be updated, we will have to manually keep our local file updated. A better solution is to use overlays: they enable us modify just what we need on top of already existing derivations. The syntax is the following:
(
final: prev: {
}
)
final represent the result that we want to obtain (version attribute
modified), and prev
is the original state of our derivation (the source from nixpkgs).
(
final: prev: {
dwl = (prev.dwl.overrideAttrs {
}
}
))
Then in the body we can assign dwl to the previous value of dwl that we access via
prev.dwl. But here we also call overrideAttrs which is a function that
lets you override attributes as the name says.
( final: prev: {
dwl = (prev.dwl.overrideAttrs {
# Fetching main because my patches require the last version
version = "main";
src = pkgs.fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = final.dwl.version;
hash = "sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=";
};
});
})
Then we add our modifications, notice that we access the modified version value with
final.dwl.version.
# configuration.nix
{ ... }:
{
nixpkgs.overlays = [
( final: prev: {
dwl = (prev.dwl.overrideAttrs {
# Fetching main because my patches require the last version
version = "main";
src = pkgs.fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = final.dwl.version;
hash = "sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=";
};
});
})
]
environment.systemPackages = with pkgs; [
dwl
# ...
]
}
Finally, we can but this derivation in nixpkgs.overlays which takes a list of
overlays, so you could do that for any number of packages that you want. And don't forget to add
your package in the systemPackages! Note that it's possible, and maybe cleaner, to write your
overlays in a separated file, just omit the parentheses around the block:
# overlays.nix
final: prev: {
dwl = (prev.dwl.overrideAttrs {
# Fetching main because my patches require the last version
version = "main";
src = pkgs.fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = final.dwl.version;
hash = "sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=";
};
});
}
And import the file:
# configuration.nix
{ ... }:
{
nixpkgs.overlays = [
(import ./overlays.nix)
];
environment.systemPackages = with pkgs; [
dwl
# ...
]
}
Here you go!
As a final note, sometimes you may also want to change the parameters of a derivation. This is
what the override function does. In my example I want to specify the source of my dwl
config file, dwl-config.h. In this derivation its controlled with the
configH parameter (here its also handling an old option called simply conf).
{
# ...
configH ?
if conf != null then
lib.warn ''
conf parameter is deprecated;
use configH instead
'' conf
else
null,
# Deprecated options
# Remove them before next version of either Nixpkgs or dwl itself
conf ? null,
}:
So you can use override as follows:
dwl = ((prev.dwl.overrideAttrs {
# Fetching main because my patches require the last version
version = "main";
src = prev.fetchFromGitea {
domain = "codeberg.org";
owner = "dwl";
repo = "dwl";
rev = final.dwl.version;
hash = "sha256-qO7k2Sj4nWrXrM2FwNkgnAK2D76bIWa2q625k3jDBUA=";
};
}).override {
# My own config
configH = ./dwl-config.h;
});
I just added a pair of parentheses to wrap everything. In conclusion:
overrideAttrs {} is a function to let you modify the attributesoverride {} on the other hand will act on the parameters define at the top of the
file i.e. { ... }: