Nix Cookbook

From NixOS Wiki
Jump to: navigation, search

Environment Tasks

Creating Shell Scripts

Arbitrary system shell scripts can be created with pkgs.writeScriptBin. It creates a derivation which you add to environment.systemPackages.

{ pkgs, ... }:

let
  helloWorld = pkgs.writeScriptBin "helloWorld" ''
    #!${pkgs.stdenv.shell}
    echo Hello World
  '';

in {
  environment.systemPackages = [ helloWorld ];
}

Creating Periodic Services

Using the systemd support periodic services can be defined. In this case a service named simple-timer writes out the current time to /tmp/simple-timer.log every minute.

{ pkgs, ... }:

{
  systemd = {
    timers.simple-timer = {
      wantedBy = [ "timers.target" ];
      partOf = [ "simple-timer.service" ];
      timerConfig.OnCalendar = "minutely";
    };
    services.simple-timer = {
      serviceConfig.Type = "oneshot";
      script = ''
        echo "Time: $(date)." >> /tmp/simple-timer.log
      '';
    };
  };
}

Wrapping packages

If you need to wrap a binary of a package (or a non-binary), there are a few ways of doing it. The simplest of which is just creating a new binary that calls the old one:

pkgs.writeScriptBin "hello" ''
  #!${pkgs.stdenv.shell}
  # Call hello with a traditional greeting 
  exec ${pkgs.hello}/bin/hello -t
''

The disadvantage of this way is that it doesn't propagate man pages and other paths from the old derivation. There are multiple ways of solving that:

let
  wrapped = pkgs.writeScriptBin "hello" ''
    #!${pkgs.stdenv.shell}
    exec ${pkgs.hello}/bin/hello -t
  '';
in

pkgs.symlinkJoin {
  name = "hello";
  paths = [
    wrapped
    pkgs.hello
  ];
}

Similarly the following works too:

pkgs.symlinkJoin {
  name = "hello";
  paths = [ pkgs.hello ];
  buildInputs = [ pkgs.makeWrapper ];
  postBuild = ''
    wrapProgram $out/bin/hello \
      --add-flags "-t"
  '';
}

If you prefer not to have every file symlinked and have a cleaner result, the following is also possible:

pkgs.runCommand "hello" {
  buildInputs = [ pkgs.makeWrapper ];
} ''
  mkdir $out
  # Link every top-level folder from pkgs.hello to our new target
  ln -s ${pkgs.hello}/* $out
  # Except the bin folder
  rm $out/bin
  mkdir $out/bin
  # We create the bin folder ourselves and link every binary in it
  ln -s ${pkgs.hello}/bin/* $out/bin
  # Except the hello binary
  rm $out/bin/hello
  # Because we create this ourself, by creating a wrapper
  makeWrapper ${pkgs.hello}/bin/hello $out/bin/hello \
    --add-flags "-t"
''

And lastly, there is the possibility of wrapping things right inside the derivation you want to wrap, this is however discouraged and impractical in most cases, as it requires recompilation of it:

pkgs.hello.overrideAttrs (oldAttrs: {
  buildInputs = oldAttrs.buildInputs or [] ++ [ pkgs.makeWrapper ];
  postInstall = oldAttrs.postInstall or "" + ''
    wrapProgram $out/bin/hello \
      --add-flags "-t"
  '';
})

Debugging

Common Errors

Bad configuration option: gssapikexalgorithms

Found when using an SSH binary from Nix on typically RPM-based distros like CentOS, Fedora, Scientific Linux, Redhat, etc. The quick fix: Just comment out the configuration option in the ssh config file, you probably don't need it.

Desktop Environment does not find .desktop files

IF your DE does not look in $HOME/.nix-profile/share for .desktop files. You need to add that path to the XDG_DATA_DIRS, the position reflects precedence so files in earlier directories shadow files in later directories. This can be accomplished in various ways depending on your login manager, see Arch wiki: Xprofile for more information. For example using ~/.xprofile as follows:

$ export XDG_DATA_DIRS=$HOME/.nix-profile/share:/usr/local/share:/usr/share

Notice that you have to include the default locations on your system, otherwise they will be overwritten. Find out the proper paths using echo $XDG_DATA_DIRS. (Note: export XDG_DATA_DIRS=$HOME/.nix-profile/share:$XDG_DATA_DIRS did not work, XDG_DATA_DIRS ended up containing only $HOME/.nix-profile/share: which isn't even a valid path.)