FAQ/Pinning Nixpkgs: Difference between revisions

imported>Grahamc
Complete the page on pinning and patching Nixpkgs.
DHCP (talk | contribs)
m use '$' and lang=console for shell commands syntax highlighting; update links to channels.nixos.org
 
(26 intermediate revisions by 16 users not shown)
Line 1: Line 1:
{{FAQ/breadcrumb}}
It is possible (and indeed, fairly easy) to pin a specific version of
It is possible (and indeed, fairly easy) to pin a specific version of
Nixpkgs. This can be used to upgrade individual applications
Nixpkgs. This can be used to upgrade individual applications
Line 4: Line 5:
not impacted by other systems' requirements.
not impacted by other systems' requirements.


For example, the following code uses the host's Nixpkgs as a
Another reason why one would want to pin nixpkgs is to get older versions of a specific software. [https://lazamar.co.uk/nix-versions/ This site] can show you all the versions a package went through, and what nixpkgs revision to use to get your specific version.
springboard to fetch and import a specific, pinned version of Nixpkgs.
This is safe because the specific code we're using from the variable
host Nixpkgs is using a very stable API, and will be thrown away as
soon as we are done importing the pinned version of Nixpkgs.


Where before you would use <code>pkgs = import <nixpkgs> {}</code>
Note: You can <code>sudo nix-channel --remove nixpkgs</code>, but you still need a nix-channel for nixos
(which uses the host's Nixpkgs version) you can pin to an exact
version of Nixpkgs by instead using:


Be aware that this also pins all dependencies of the application which often causes issues for GUI applications and also brings in back outdated and potentially vulnerable dependencies.
<syntaxhighlight lang="console">
$ sudo nix-channel --list
nixos https://channels.nixos.org/nixos-26.05
</syntaxhighlight>
Nix 2.0 introduces new builtins, <code>fetchTarball</code> and <code>fetchGit</code>, which make it possible to fetch a specific version of nixpkgs without depending on an existing one:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
pkgs = let
import (builtins.fetchTarball {
  hostPkgs = import <nixpkgs> {};
   # Descriptive name to make the store path easier to identify
   pinnedPkgs = hostPkgs.fetchFromGitHub {
  name = "nixos-unstable-2018-09-12";
    owner = "NixOS";
  # Commit hash for nixos-unstable as of 2018-09-12
    repo = "nixpkgs-channels";
  url = "https://github.com/NixOS/nixpkgs/archive/ca2ba44cab47767c8127d1c8633e2b581644eb8f.tar.gz";
    # nixos-unstable as of 2017-11-13T08:53:10-00:00
  # Hash obtained using `nix-prefetch-url --unpack <url>`
    rev = "ac355040656de04f59406ba2380a96f4124ebdad";
  sha256 = "1jg7g6cfpw8qvma0y19kwyp549k1qyf11a5sg6hvn6awvmkny47v";
    sha256 = "0frhc7mnx88sird6ipp6578k5badibsl0jfa22ab9w6qrb88j825";
}) { }
  };
in import pinnedPkgs {};
</syntaxhighlight>
</syntaxhighlight>


This can also be instead used to pull nixpkgs from an internal fork of
Or, to use git for fetching:
Nixpkgs, with your own changes on top. Note, however, as it stands
 
Nix 1.11 has difficulties fetching repositories which require
<syntaxhighlight lang="nix">
authentication, this is to be fixed in Nix 1.12.
import (builtins.fetchGit {
  # Descriptive name to make the store path easier to identify
  name = "nixos-unstable-2018-09-12";
  url = "https://github.com/NixOS/nixpkgs/";
  # Commit hash for nixos-unstable as of 2018-09-12
  # `git ls-remote https://github.com/NixOS/nixpkgs nixos-unstable`
  ref = "refs/heads/nixos-unstable";
  rev = "ca2ba44cab47767c8127d1c8633e2b581644eb8f";
}) { }
</syntaxhighlight>


The package <code>nix-prefetch-git</code> can be used to automatically
If the <code>ref</code> attribute is omitted, we get an error like this:
calculate the current version and hash of a branch, and output the
information to a file:


<syntaxhighlight>
<syntaxhighlight>
$ nix-shell -p nix-prefetch-git
fatal: not a tree object: 3d70d4ba0b6be256974910e635fadcc0e9579b2a
error: while evaluating the attribute 'buildInputs' of the derivation 'nix-shell' at /nix/store/b93cq865x6qxpn4dw9ivrk3yjcsm8r97-nixos-19.09/pkgs/build-support/mkshell/default.nix:28:3:
[nix-shell:~]$ nix-prefetch-git https://github.com/nixos/nixpkgs-channels.git refs/heads/nixos-unstable > nixpkgs-version.json
while evaluating 'getOutput' at /nix/store/b93cq865x6qxpn4dw9ivrk3yjcsm8r97-nixos-19.09/lib/attrsets.nix:464:23, called from undefined position:
while evaluating anonymous function at /nix/store/b93cq865x6qxpn4dw9ivrk3yjcsm8r97-nixos-19.09/pkgs/stdenv/generic/make-derivation.nix:142:17, called from undefined position:
...
program 'git' failed with exit code 128
</syntaxhighlight>
[nix-shell:~]$ cat nixpkgs-version.json
 
{
== Pinning an unstable service ==
  "url": "https://github.com/nixos/nixpkgs-channels.git",
How to upgrade a single package and service to an unstable version
   "rev": "f607771d0f5e4fa905afff1c772febd9f3103e1a",
 
   "date": "2018-01-09T11:18:25-05:00",
There is probably a better way, especially once flakes come around. Some packages let you specify which <code>package</code> to run as an option but most don't. The following is a generic way that also works for those which don't.
   "sha256": "1icphqpdcl8akqhfij2pxkfr7wfn86z5sr3jdjh88p9vv1550dx7",
 
   "fetchSubmodules": true
add to configuration.nix a set allowing unstable packages.
}
This assumes a channel named <code>nixpkgs-unstable</code> exists, like so:
<syntaxhighlight lang="console">
$ nix-channel --add https://channels.nixos.org/nixpkgs-unstable nixpkgs-unstable
$ nix-channel --update
</syntaxhighlight>
 
then in <code>configuration.nix</code> allow unstable packages:
<syntaxhighlight lang="nix">
# Allow unstable packages.
nixpkgs.config = {
   allowUnfree = true;
   packageOverrides = pkgs: {
    unstable = import <nixpkgs-unstable> {
      config = config.nixpkgs.config;
    };
   };
};
</syntaxhighlight>
 
This means you can now refer to unstable packages as <code>pkgs.unstable.nameofpackage</code> which is great. 
For example:
 
<syntaxhighlight lang="nix">
environment.systemPackages = with pkgs; [
  unstable.bind
  unstable.dnsutils
   vim
];
</syntaxhighlight>
</syntaxhighlight>
 
This file can then be used to specify the version of Nixpkgs:
This will use unstable bind and dnsutils, but the stable vim.
 
Except bind is a service, and if you want a service....usually you just do something like:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
pkgs = let
services.bind.enable = true;
  hostPkgs = import <nixpkgs> {};
...
  pinnedVersion = hostPkgs.lib.importJSON ./nixpkgs-version.json
  pinnedPkgs = hostPkgs.fetchFromGitHub {
    owner = "NixOS";
    repo = "nixpkgs-channels";
    inherit (pinnedVersion) rev sha256;
  };
in import pinnedPkgs {};
</syntaxhighlight>
</syntaxhighlight>


Finally, this can be taken a step further, and you can apply extra
Except services will refer to <code>pkgs.bind</code>, not <code>pkgs.unstable.bind</code>
patches to the pinned version of Nixpkgs, for perhaps PRs that are not
 
yet merged, or private internal changes that you need. If you take
so disable services.bind and create your own:
this route, probably best to move the following in to its own file
that you then import.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
pkgs = let
users.users.named = {
  hostPkgs = import <nixpkgs> {};
  uid = config.ids.uids.bind;
  pinnedVersion = hostPkgs.lib.importJSON ./nixpkgs-version.json
  description = "BIND daemon user";
  pinnedPkgs = hostPkgs.fetchFromGitHub {
};
    owner = "NixOS";
systemd.services.mybind = {
    repo = "nixpkgs-channels";
  description = "BIND Domain Name Server";
    inherit (pinnedVersion) rev sha256;
  unitConfig.Documentation = "man:named(8)";
  };
  after = [ "network.target" ];
  wantedBy = [ "multi-user.target" ];
  patches = [
  preStart = ''
    ./patches/0001-my-nixpkgs-change.patch
    mkdir -m 0755 -p /etc/bind
  ];
    if ! [ -f "/etc/bind/rndc.key" ]; then
      ${pkgs.unstable.bind.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u named -a -A hmac-sha256 2>/dev/null
  patchedPkgs = hostPkgs.runCommand "nixpkgs-${pinnedVersion.rev}"
    fi
    {
    ${pkgs.coreutils}/bin/mkdir -p /run/named
      inherit pinnedPkgs;
    chown named /run/named
      inherit patches;
  '';
    }
  serviceConfig = {
    ''
    ExecStart = "${pkgs.unstable.bind.out}/sbin/named -u named -4 -c /etc/bind/named.conf -f";
      cp -r $pinnedPkgs $out
    ExecReload = "${pkgs.unstable.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
      chmod -R +w $out
    ExecStop = "${pkgs.unstable.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
      for p in $patches; do
  };
        echo "Applying patch $p";
};
        patch -d $out -p1 < "$p";
      done
    '';
in import patchedPkgs {};
</syntaxhighlight>
</syntaxhighlight>
where all the stuff just comes from the bind services definition(which you can get from the source link on the nixos options page.)
Just replace named variables, and replace <code>${pkgs.bind.out</code> with <code>${pkgs.unstable.bind.out}</code>
== See also ==
* [https://nix.dev/reference/pinning-nixpkgs Pinning Nixpkgs]
* [https://nix.dev/tutorials/first-steps/towards-reproducibility-pinning-nixpkgs Towards Reproducibility: Pinning Nixpkgs]
* [https://nix.dev/guides/recipes/dependency-management.html Dependency Management]