Nix

Nix is not a configuration management tool alike Puppet or Chef. It is more accurately described as a (universal) package manager or build tool.

Nix operates within its store (usually located in '/nix') to gather packages called derivations. In that regard, unless you are running nixos, nix won’t configure systemd services for instance.

Nix aims to offer:

  • best possible build reproducibility

  • self-contained environments

  • easy rollback

  • composability

Tips and tricks

Overlays

You can override derivation attributes in user space without forking the nixpkgs repository by using one or multiple overlays:

~/.config/nixpgks/overlays/default.nix
self: super:
let
  hlib = super.haskell.lib;
in
{
  haskellPackages = super.haskellPackages.override {
    overrides =  curr: prev: {
      cicd-shell = hlib.dontCheck (hlib.dontHaddock
        (curr.callCabal2nix "cicd-shell" (super.fetchgit { (1)
           url = "http://stash.cirb.lan/scm/cicd/cicd-shell.git";
           rev = "d76c532d69e4d01bdaf2c716533d9557371c28ea";
           sha256 = "0yval6k6rliw1q79ikj6xxnfz17wdlnjz1428qbv8yfl8692p13h";
         }) {
              protolude = prev.protolude_0_2;
            }
        ));
      };
    };
}
1 callCabal2nix allows to automatically fetch and build any haskell package from the web
Pinned a version of nixpkgs
let
  nixpkgs = builtins.fromJSON (builtins.readFile ./.nixpkgs.json);
in
import (fetchTarball {
  url = "https://github.com/NixOS/nixpkgs/archive/${nixpkgs.rev}.tar.gz";
  inherit (nixpkgs) sha256;
})

Updating .nixpkgs.json is realized with such a zsh function:

function updateNixpkgs () {
    nix-prefetch-git https://github.com/NixOS/nixpkgs.git "$1" > ~/.config/nixpkgs/.nixpkgs.json
}

It is easier to manage pinned/sha using niv.

Private packages

You can also extend nixpkgs with private derivations without any forking. For instance using a custom file:

dotfiles.nix
with import <nixpkgs> {}; (1)

let xmonadEnv = haskellPackages.ghcWithPackages (p: with p; [xmonad xmonad-contrib]); (2)
in

stdenv.mkDerivation {
  name = "devbox_dotfiles-0.1";

  src = fetchFromGitHub {
    owner = "CIRB";
    repo = "devbox-dotfiles";
    rev = "801f66f3c7d657f5648963c60e89743d85133b1a" ;
    sha256 = "1w4vaqp21dmdd1m5akmzq4c3alabyn0mp94s6lqzzp1qpla0sdx0" ;
  };

  buildInputs = [ xmonadEnv ];

  installPhase = ''
    ${xmonadEnv}/bin/ghc --make .xmonad/xmonad.hs -o .xmonad/xmonad-x86_64-linux (3)
    cp -R ./. $out (4)
  '';

  meta = {
    description = "Dot files for the devbox";
  };
}
1 dependencies provided by nixpkgs using $NIX_PATH
2 ghc with module deps included
3 at this stage, the shell is inside a temp dir with the src included
4 copy the content of the current dir into $out

You then build the derivation or install it in the user environment.

→ nix-build dotfiles.nix
→ nix-env -f dotfiles.nix -i devbox_dotfiles (1)
1 nix-env -i takes the name attribute and strip the version (first numeric after -)
Overrides haskell packages for the ghc821 compiler
self: super:
let
  hlib = super.haskell.lib;
in
{
haskell = super.haskell // { packages = super.haskell.packages // { ghc821 = super.haskell.packages.ghc821.override { (1)
   overrides =  hpkgs: _hpkgs: {
     containers = hlib.dontCheck(_hpkgs.containers);
   };
};};};
}
1 haskell equals super.haskell except packages, which equals super.haskell.packages except for ghc821, which is the overriden version of super 821
Caching the list of all available package into a local file
nix-env -qaP --description '*' > ~/allpkgs.desc
Reproduce any hydra build locally
bash <(curl https://hydra.nixos.org/build/57055021/reproduce)
Installed nixpkgs version

nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version'

Refering to an attribute that contains dot (e.g: foo-1.2.0)

You will need to escape the .:

nix-build -A '"foo-1.2.0"'
Convert an attribute set into a string representation

Attribute set are not automatically converted into string. That said, you can provide an implementation by using the outPath field:

builtins.fromJSON fromCUE // { outPath = fromCUE; }

Pitfall

When you create a derivation, a path value will auto-magically be interpolated with its generated outpath:

pkgs.runCommand "hello" { buildInputs = []; } ''
  cp ${./talk.adoc} $out (1)
''
1 path → /nix/store/xxxx

The tricky part arises when you want to use a string variable to describe this path. To force the coersing from string → path you need to use +:

let
  cueSchema = "${config.home.homeDirectory}/bootstrap/user/schema.cue";
  cueConfig = "${sharedDir}/box.cue";
in
pkgs.runCommand "fromCue" { } ''
  ${pkgs.cue}/bin/cue export ${/. + cueSchema } ${/. + cueConfig} > $out;
''