FAQ/Pinning Nixpkgs

From NixOS Wiki
Jump to: navigation, search

It is possible (and indeed, fairly easy) to pin a specific version of Nixpkgs. This can be used to upgrade individual applications separately on their own terms, and to ensure their deployability is not impacted by other systems' requirements.

Nix 2.0 onwards

Nix 2.0 introduces new builtins, fetchTarball and fetchGit, which make it possible to fetch a specific version of nixpkgs without depending on an existing one:

import (builtins.fetchTarball {
  # Descriptive name to make the store path easier to identify
  name = "nixos-unstable-2018-09-12";
  # Commit hash for nixos-unstable as of 2018-09-12
  url = https://github.com/nixos/nixpkgs/archive/ca2ba44cab47767c8127d1c8633e2b581644eb8f.tar.gz;
  # Hash obtained using `nix-prefetch-url --unpack <url>`
  sha256 = "1jg7g6cfpw8qvma0y19kwyp549k1qyf11a5sg6hvn6awvmkny47v";
}) {}

Or, to use git for fetching (this has the advantage of being somewhat faster for updates, but is slower for the initial fetch):

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-channels nixos-unstable`
  rev = "ca2ba44cab47767c8127d1c8633e2b581644eb8f";
}) {}

Before 2.0

The following code uses the host's Nixpkgs as a 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 pkgs = import <nixpkgs> {} (which uses the host's Nixpkgs version) you can pin to an exact version of Nixpkgs by instead using:


pkgs = let
  hostPkgs = import <nixpkgs> {};
  pinnedPkgs = hostPkgs.fetchFromGitHub {
    owner = "NixOS";
    repo = "nixpkgs-channels";
    # nixos-unstable as of 2017-11-13T08:53:10-00:00
    rev = "ac355040656de04f59406ba2380a96f4124ebdad";
    sha256 = "0frhc7mnx88sird6ipp6578k5badibsl0jfa22ab9w6qrb88j825";
  };
in import pinnedPkgs {}

This can also be instead used to pull nixpkgs from an internal fork of Nixpkgs, with your own changes on top. Note, however, as it stands Nix 1.11 has difficulties fetching repositories which require authentication, this is to be fixed in Nix 1.12.

The package nix-prefetch-git can be used to automatically calculate the current version and hash of a branch, and output the information to a file:

$ nix-shell -p nix-prefetch-git
 
[nix-shell:~]$ nix-prefetch-git https://github.com/nixos/nixpkgs-channels.git refs/heads/nixos-unstable > nixpkgs-version.json
 
...
 
[nix-shell:~]$ cat nixpkgs-version.json
{
  "url": "https://github.com/nixos/nixpkgs-channels.git",
  "rev": "f607771d0f5e4fa905afff1c772febd9f3103e1a",
  "date": "2018-01-09T11:18:25-05:00",
  "sha256": "1icphqpdcl8akqhfij2pxkfr7wfn86z5sr3jdjh88p9vv1550dx7",
  "fetchSubmodules": true
}

This file can then be used to specify the version of Nixpkgs:

pkgs = let
   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 {};

Finally, this can be taken a step further, and you can apply extra 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 this route, probably best to move the following in to its own file that you then import.

pkgs = let
   hostPkgs = import <nixpkgs> {};
   pinnedVersion = hostPkgs.lib.importJSON ./nixpkgs-version.json;
   pinnedPkgs = hostPkgs.fetchFromGitHub {
     owner = "NixOS";
     repo = "nixpkgs-channels";
     inherit (pinnedVersion) rev sha256;
   };
 
   patches = [
     ./patches/0001-my-nixpkgs-change.patch
   ];
 
   patchedPkgs = hostPkgs.runCommand "nixpkgs-${pinnedVersion.rev}"
     {
       inherit pinnedPkgs;
       inherit patches;
     }
     ''
       cp -r $pinnedPkgs $out
       chmod -R +w $out
       for p in $patches; do
         echo "Applying patch $p";
         patch -d $out -p1 < "$p";
       done
     '';
 in import patchedPkgs {};