Nix: the functional Package Manager

and

@ Lambda World Cádiz 2015

declarative Linux distribution

NixOS

Problem?

Functional approach

GHC runtime dependency graph

Nix

  • purely functional language to describe how to build packages and their dependencies
  • build results only depend on declared inputs.
  • packages never change after they have been built (they're immutable)
  • "The Purely Functional Software Deployment Model" - Eelco Dolstra PhD thesis (2006)
  • solves dependency hell set of problems

Nix

  • lazy evaluated, dynamically typed
  • C++
  • runs on POSIX (Linux / OS X / FreeBSD/ Cygwin)
  • (could work on Windows, if there was interest)
  • multi-user support
  • source/binary model
$ du -sh $(nix-store -qR `nix-build -A ghc`)
5.1M    /nix/store/5l472kj6h5zvccpzp0zcdpc1w2njfd58-linux-headers-3.12.32
34M     /nix/store/hd6km3hscbgl2yw8nx7lr5z9s8h89p04-glibc-2.21
364K    /nix/store/31w31mc8immhpnmxvcl4l0fvc3i5iwh0-zlib-1.2.8
5.1M    /nix/store/xag5ayq906w9zhlxs8wayv4kvpiyqphq-bash-4.3-p42
12M     /nix/store/4i01mpa2s194m44xrz41wmf4yr49jysq-ncurses-5.9
336K    /nix/store/8z667vv1agvpd3iknmk94j0ix6bv413i-attr-2.4.47
472K    /nix/store/sk0b1r840b686zc2m8mzyw8yyq1aymqh-acl-2.2.52
16M     /nix/store/8xfxnm4a7jvyh0i1sd967z6rws8lh21a-coreutils-8.24
98M     /nix/store/i9nn1fkcy95dzf0hb9wi8gbkid3iw1sa-gcc-4.9.3
21M     /nix/store/kxf1p7l7lgm6j5mjzkiwcwzc98s9f1az-binutils-2.23.1
92K     /nix/store/4n9yj90rdggqcx34ylbshryg3rf9cqpj-gcc-wrapper-4.9.3
46M     /nix/store/a3x9hggwq83f3fk88lqvgh6jvlklhk36-perl-5.20.2
2.7M    /nix/store/i4g323b60pskbayk4awl3ffz8m365264-gmp-5.1.3
999M    /nix/store/7hnlpxpmpka84zrhyq7z11bs0q3vgak1-ghc-7.10.2

GHC closure (runtime dependencies)

$ grep -R /nix/store/a3x9...-perl-5.20.2 /nix/store/7hnl...-ghc-7.10.2
/nix/store/7hnl...-ghc-7.10.2/lib/ghc-7.10.2/ghc-split:#!/nix/store/a3x9...-perl-5.20.2/bin/perl
/nix/store/7hnl...-ghc-7.10.2/lib/ghc-7.10.2/settings: ("perl command", "/nix/store/a3x9...-perl-5.20.2/bin/perl"),

Perl?!

$ tree `nix-build -A zlib`
/nix/store/31w31mc8immhpnmxvcl4l0fvc3i5iwh0-zlib-1.2.8
├── include
│   ├── zconf.h
│   └── zlib.h
├── lib
│   ├── libz.a
│   ├── libz.so -> libz.so.1.2.8
│   ├── libz.so.1 -> libz.so.1.2.8
│   ├── libz.so.1.2.8
│   └── pkgconfig
│       └── zlib.pc
└── share
    └── man
        └── man3
            └── zlib.3.gz

$PREFIX

$ ldd `which perl`
        linux-vdso.so.1 (0x00007ffc9fdd0000)
        libperl.so => /nix/store/lhgkssg79bxdba541h75mgdvjh35b8ic-perl-5.20.2/lib/perl5/5.20.2/x86_64-linux-thread-multi/CORE/libperl.so (0x00007fa537f3b000)
        libnsl.so.1 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libnsl.so.1 (0x00007fa537d23000)
        libdl.so.2 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libdl.so.2 (0x00007fa537b1f000)
        libm.so.6 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libm.so.6 (0x00007fa53781c000)
        libcrypt.so.1 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libcrypt.so.1 (0x00007fa5375e1000)
        libutil.so.1 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libutil.so.1 (0x00007fa5373de000)
        libpthread.so.0 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libpthread.so.0 (0x00007fa5371c1000)
        libc.so.6 => /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/libc.so.6 (0x00007fa536e21000)
        /nix/store/npfsi1d9ka8zwnxzn3sr08hbwvpapyk7-glibc-2.21/lib/ld-linux-x86-64.so.2 (0x00007fa5382f4000)

Dynamic libraries are linked deterministically

Profiles and User Environments

$ cat default.nix
derivation {
  name = "my-package";
  builder = ./builder.sh;
  system = "x86-64-linux";
  src = /home/user/bla.tar.gz;
}
$ cat builder.sh
tar xvfz $src
cd plan9port/
mkdir $out
cp planport.sh $out/bin/planport
$ nix-build
{ stdenv, fetchurl, fetchgit, openssl, zlib, pcre, libxml2 }:

stdenv.mkDerivation rec {
  name = "nginx-${version}";
  version = "1.4.4";

  src = fetchurl {
    url = "http://nginx.org/download/nginx-${version}.tar.gz";
    sha256 = "1f82845mpgmhvm151fhn2cnqjggw9w7cvsqbva9rb320wmc9m63w";
  };

  buildInputs = [ openssl zlib pcre libxml2 ];
  configureFlags = [ "--with-http_spdy_module" ];
  postInstall = "mv $out/sbin $out/bin";

  meta = with stdenv.lib; {
    description = "A reverse proxy and lightweight webserver";
    maintainers = [ maintainers.domenkozar ];
    platforms = platforms.all;
    license = licenses.bsd2;
  };
}

Purity

src = fetchurl {
  url = "http://nginx.org/download/nginx-${version}.tar.gz";
  sha256 = "1f82845mpgmhvm151fhn2cnqjggw9w7cvsqbva9rb320wmc9m63w";
};

fixed output derivation

/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-nginx-1.5.13/

  • functional language
  • chroot
  • disabled networking
  • patch shebangs
  • /nix/store/ mounted as read-only
  • patches to tooling

A package is an output of a pure function (it depends only on the function inputs, without any side effects) 

Lazy language (hackable)

Override lambda function parameters for the file

libreNginx = nginx.override { openssl = libressl; }

Override derivation attributes

myNginx = nginx.overrideDerivation  (super:
  name = "nginx-git";
  src = fetchFromGitHub {
    sha256 = "04s7xcgmi5g58lirr48vf203n1jwdxf981x1p6ysbax24qwhs2kd";
    rev = "c0eb2f0759726f47bd06f5c8f46739f43ce55cac";
    repo = "nginx";
    owner = "nginx";
  };
})
$ git clone https://github.com/NixOS/nixpkgs.git && cd nixpkgs
$ git grep "nginx = "
pkgs/top-level/all-packages.nix:  nginx = callPackage ../servers/http/nginx { };

nix-shell - start an interactive shell based on a Nix expression

Haskell

  • ~50% of the packages available from Hackage
  • well over 8,000 builds in total

  • Stack support

  • 60+ Haskell package sets

  • maintained by Peter Simons

$ nix-build
/nix/store/4jzj9qwknqz24cx6zvn7y4cmnq19hvv4-my-ghc-app-0.0.1
$ ls -la result
result -> /nix/store/4jzj9qwknqz24cx6zvn7y4cmnq19hvv4-my-ghc-app-0.0.1

$ ./result/bin/mybin
>>>
$ nix-shell --pure
$ nix-copy-closure --sign result/ domenkozar@example.com

Build the app

Run it

Develop the app

Deploy it

Toolset

$ cabal2nix --shell . > shell.nix

Generate Nix expresssion based on Cabal

NixOS: packages + systemd + Linux kernel

{
  boot.loader.grub.device = "/dev/sda";
  fileSystems."/".device = "/dev/sda1";

  networking.firewall = {
    enable = true;
    allowedTCPPorts = [ 80 ];
  };

  environment.systemPackages = with pkgs; [
    wget
    git
    gnupg
    tmux
  ];
  
  services = {
    sshd.enable = true;
    munin-node.enable = true;
    munin-cron = {
      enable = true;
      hosts = ''
        [${config.networking.hostName}]
        address localhost
      '';
    };
  };
}

NixOS modules

{ config, lib, pkgs, ... }:

let
  configFile = pkgs.writeText "nginx.conf" ''
    user nginx nginx
    daemon off;
  '';
in {
  options = {
    services.nginx = {
      enable = lib.mkOption {
        default = false;
        description = "Enable the nginx Web Server.";
        types = types.bool;
      };
    };
  };

  config = mkIf config.services.nginx.enable {
    environment.systemPackages = [ pkgs.nginx ];

    systemd.services.nginx = {
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      path = [ pkgs.nginx ];
      serviceConfig = {
        ExecStart = "${nginx}/bin/nginx -c ${configFile}";
      };
    };
  };
}

Rollback!

Getting started with NixOps

$ nix-env -i nixops

trivial.nix

{
  webserver = { config, pkgs, ... }:
    { services.httpd.enable = true;
      services.httpd.adminAddr = "alice@example.org";
      services.httpd.documentRoot = "${pkgs.valgrind}/share/doc/valgrind/html";
    };
}

trivial-virtualbox.nix

{
  webserver = { config, pkgs, ... }:
    { deployment.targetEnv = "virtualbox";
      deployment.virtualbox.memorySize = 1024; # megabytes
    };
}

Provision and deploy

$ nixops create ./trivial.nix ./trivial-virtualbox.nix -d trivial
33bced96-5f26-11e1-b9d7-9630d48abec1

$ nixops deploy -d trivial
creating VirtualBox VM ‘webserver’...
...

Hydra build farm

Challanges

  • private nix store files
  • P2P / IPFS distribution of binary packages
  • statically typed Nix (2.0?)
  • get rid of Perl as dependency in Nix source
  • native Windows support
  • multiple outputs
  • binary determinism
  • low-level speed/memory improvements
  • GuixSD

 

conf.nixos.org

14th and 15 November @ Berlin

2 days of talks by core contributors

Thanks!

Nix the functional Package Manager and NixOS the declarative Linux distribution

By Domen Kožar

Nix the functional Package Manager and NixOS the declarative Linux distribution

  • 4,519