Notes on Nix

June 05, 2015

Nix the package manager

nix-env

To search packages in the haskellPackages namespace:

$ nix-env -f "<nixpkgs>" -qaP -A haskellPackages

To install packages, which live in the haskellPackages namespace, use -A:

$ nix-env -f "<nixpkgs>" -i -A haskellPackages.ghc

See User's Guide to the Haskell Infrastructure for more information.

To fix various libz.so problems, use nix-shell:

$ nix-shell -p zlib zlibStatic

To use the latest Agda, pull the latest nixpkgs and install directly:

$ nix-env -f /path/to/nixpkgs -i -A haskellPackages.Agda

To list all known Haskell compilers in Nix:

$ nix-instantiate --eval -E "with import <nixpkgs> {}; lib.attrNames haskell.compiler"

nix-repl can also be used to do this:

$ nix-env -i nix-repl
$ nix-repl
nix-repl> :l <nixpkgs>
nix-repl> haskell.compiler.ghc<Tab>

nix-shell as a package prophylactic

Use nix-shell to set up a temporary environment with dependencies that aren't needed in your daily environment:

$ nix-shell -p ruby --run "gem install --user vimgolf"

Let's say I want to do a thing, but that thing requires stuff:

$ git clone https://github.com/mszep/pandoc_resume
$ cd pandoc_resume
$ vim resume.md
$ make resume.pdf
make: command not found

Well, shoot. I don't have make installed. Even if I did, this project's Makefile requires both pandoc and context, and I don't have those installed either.

I don't really want to have to install make, pandoc, and context just to try this thing out. With Nix, I don't have to.

Let's try running make again, letting Nix handle the dependencies:

$ nix-shell -p gnumake pandoc texLiveFull --run 'make resume.pdf'

It worked! But now I have make, pandoc, and context installed, right? I didn't want that.

$ make
make: command not found

$ pandoc
pandoc: command not found

$ context
context: command not found

Nope! Immutable configuration is immutable.

Note: context might need mtxrun --generate to be run once before it's used.

Haskell via nix-shell -p

~/.bashrc:

function haskell-shell() {
  nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [$@])"
}

Example:

$ haskell-shell random
[nix-shell]$ ghci
Prelude> System.Random.randomRIO (1, 100)
42

Haskell via derivation

Main.hs:

import System.Console.ANSI

main = do
    setSGR [ SetConsoleIntensity NormalIntensity
           , SetColor Foreground Vivid White
           , SetColor Background Dull Blue
           ]
    putStr "Hello,"
    setSGR [Reset] 
    putStrLn " world!"

default.nix:

{ pkgs ? import <nixpkgs> {} }:
let ghcPackages = pkgs: with pkgs; [ ansi-terminal ];
    ghc = pkgs.haskellPackages.ghcWithPackages ghcPackages;
in pkgs.stdenv.mkDerivation {
  name = "hello";
  src = ./.;
  buildInputs = [ ghc ];
  buildPhase = ''
    ghc -O2 Main.hs
  '';
  doCheck = true;
  checkPhase = ''
    diff <(printf '\033[97;44mHello,\033[0m world!\n') <(./Main)
  '';
  installPhase = ''
    mkdir -pv $out/bin
    cp Main $out/bin/hello
  '';
}
$ nix-shell
[nix-shell]$ runhaskell Main.hs
Hello, world!
$ nix-build
$ ./result/bin/example
Hello, world!

Idris via nix-shell

Write an .idr file:

hello.idr:

main : IO ();
main = putStrLn "Hello, world!";

Start a shell with the Idris Haskell package and GMP:

$ nix-shell -p haskellPackages.idris gmp

Compile and run the program:

$ idris hello.idr -o hello
$ ./hello
Hello, world!

Install a Node.js package with npm install

default.nix:

{ pkgs ? import <nixpkgs> {} }:
pkgs.runCommand "codedown" { buildInputs = with pkgs; [ nodejs ]; } ''
  mkdir -pv $out/cache $out/bin
  npm --cache $out/cache install codedown@2.1.2 --prefix $out
  rm -rf $out/cache
  ln -s $out/node_modules/.bin/codedown $out/bin/codedown
''

To install the npm package, use nix-env -i:

$ nix-env -f . -i codedown
installing ‘codedown’

Search packages in the haskellPackages namespace:

$ nix-env -f "<nixpkgs>" -qaP -A haskellPackages

To install packages, which live in the haskellPackages namespace, use -A:

$ nix-env -f "<nixpkgs>" -i -A haskellPackages.ghc

See User's Guide to the Haskell Infrastructure for more information.

To fix various libz.so problems, use nix-shell:

$ nix-shell -p zlib zlibStatic

To use the latest Agda, pull the latest nixpkgs and install directly:

$ nix-env -f /path/to/nixpkgs -i -A haskellPackages.Agda

To list all known Haskell compilers in Nix:

$ nix-instantiate --eval -E "with import <nixpkgs> {}; lib.attrNames haskell.compiler"

nix-repl can also be used to do this:

$ nix-env -i nix-repl
$ nix-repl
nix-repl> :l <nixpkgs>
nix-repl> haskell.compiler.ghc<Tab>

Install Hoogle with nix-env

$ nix-env -f "<nixpkgs>" -i -A haskellPackages.hoogle
installing ‘hoogle-4.2.43’

$ hoogle data -d ~/.hoogle
0 warnings, saved to .warnings
Data generation complete
$ hoogle '(>>=)' -d ~/.hoogle
Prelude (>>=) :: Monad m => m a -> (a -> m b) -> m b
Control.Monad (>>=) :: Monad m => m a -> (a -> m b) -> m b
Control.Monad.Instances (>>=) :: Monad m => m a -> (a -> m b) -> m b

Add a new package to nixpkgs

See nixpkgs#15731

NixOS

Installation

Prepare the USB drive

Populate the USB drive

Configure UEFI

This step is only required if installing on a newer system with UEFI.

Partition the hard drive

Depending on the system, fdisk might work, or gdisk might be needed.

Using fdisk on /dev/sda:

Using gdisk on /dev/sda:

Set up encryption

# cryptsetup luksFormat /dev/sda2
# cryptsetup luksOpen /dev/sda2 enc-pv
# pvcreate /dev/mapper/enc-pv
# vgcreate vg /dev/mapper/enc-pv
# lvcreate -L 8G -n swap vg
# lvcreate -l 28355 -n root vg

Format partitions

# mkfs.fat /dev/sda1
# mkfs.ext4 -O dir_index -j -L root /dev/vg/root
# mkswap -L swap /dev/vg/swap

Mount partitions

# mount /dev/vg/root /mnt
# mkdir /mnt/boot
# mount /dev/sda1 /mnt/boot
# swapon /dev/vg/swap

Install NixOS

# nixos-generate-config --root /mnt
# vim /mnt/etc/nixos/configuration.nix
# nixos-install

Fix wpa_supplicant

Add this to /etc/wpa_supplicant.conf:

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel

Restart wpa_supplicant:

# systemctl restart wpa_supplicant

Dual booting another OS

boot.loader.grub.extraEntries = ''
  menuentry "Windows 7" {
    chainloader (hd0,1)+1
  }
'';

References:

Apache httpd and CGI

services.httpd.enable = true;
services.httpd.adminAddr = "james@localhost";
services.httpd.extraModules = ["cgi"];

services.httpd.virtualHosts =
  [
    {
      documentRoot = "/var/www/";
      extraConfig = ''
    ScriptAlias "/cgi-bin/" "/var/www/cgi-bin/"
    <Location "/haskell-cgi">
      SetHandler haskell-handler
      Action haskell-handler "/cgi-bin/haskell-cgi" virtual
    </Location>
      '';
    }
  ];

Systemd

{ pkgs, ... }:
let
  blazegraphJar = pkgs.fetchurl {
    url = "https://github.com/blazegraph/database/releases/download/BLAZEGRAPH_RELEASE_2_1_5/blazegraph.jar";
    sha256 = "043nfc6mgmd5mxmwfcfl082y96iaqnwminn4rxbizxrs3dzaxbpv";
  };
in
{
  users.extraUsers.blazegraph.isNormalUser = true;
  systemd.services.blazegraph = {
    description = "blazegraph";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      WorkingDirectory = "/home/blazegraph";
      ExecStart = "${pkgs.openjdk}/bin/java -server -Xmx2g -Djetty.port=3000 -jar ${blazegraphJar}";
      Restart = "always";
      User = "blazegraph";
    };
  };
}