Nix

Declarative & reproducible package management

Jonas Opitz

1. What is Nix

Nix (-lang)

A programming language

  • Designed for package management
  • Turing complete

Nix

A package manager for Linux and macOS

  • Declarative
  • Reproducible

Example package

{ mkDerivation, fetchFromGitHub }:

mkDerivation rec {
  name = "revealjs";
  version = "4.5.0";
  src = fetchFromGitHub {
    owner = "hakimel";
    repo = "reveal.js";
    rev = "refs/tags/${version}";
    hash = "sha256-9Q7aWgjAV37iJp6oYDz45e8J+RKwKY1Uvgg/BXwf5nQ=";
  };
  installPhase = ''
    mkdir -vp $out
    cp -r * $out
  '';
}

Nixpkgs

A collection of packages for Nix

map_repo_size_fresh.svg

Flakes

A Nix extension that

  • simplifies version management
  • enforces hermetic evaluation

Example lock file

{
  "nodes": {
    "flake-utils": {
      "inputs": {
        "systems": "systems"
      },
      "locked": {
        "lastModified": 1689068808,
        "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
        "owner": "numtide",
        "repo": "flake-utils",
        "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
        "type": "github"
      },
      "original": {
        "owner": "numtide",
        "repo": "flake-utils",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1691950488,
        "narHash": "sha256-iUNEeudc4dGjx+HsHccnGiuZUVE/nhjXuQ1DVCsHIUY=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "720e61ed8de116eec48d6baea1d54469b536b985",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "ref": "nixos-23.05",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "nixpkgs-unstable": {
      "locked": {
        "lastModified": 1691899779,
        "narHash": "sha256-IBf4KVr/UQJlzrqB2/IHtlvmwsvyIVLPerSzCPU/6Xk=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "100a1550b0e7a64b960c625b656f9229bdef5f87",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "ref": "nixos-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "flake-utils": "flake-utils",
        "nixpkgs": "nixpkgs",
        "nixpkgs-unstable": "nixpkgs-unstable"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1681028828,
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
        "owner": "nix-systems",
        "repo": "default",
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}

NixOS

A Linux operating system, building upon Nix

Example system configuration

{ config, lib, pkgs, serviceBin, port ? 8080, ... }:

let
  username = "myUser";
in
{
  config = {
    networking.firewall.allowedTCPPorts = [ port ];
    users = {
      mutableUsers = false;
      users = {
        root.password = "";
        "${username}" = {
          isSystemUser = true;
          group = username;
        };
      };
    };
    systemd.services.web-service = {
      wantedBy = [ "multi-user.target" ];
      script = ''
        ${serviceBin} --port=${builtins.toString port}
      '';
      serviceConfig.User = username;
    };
  };
}

2. Why Nix

Isolation of packages

Example

3whscbxw9z4mk95z5dn3075z7nlpvdl6-curl-8.1.2
6wnnxzh7jr7yw4hv79j2pglaag6ggcaa-curl-8.1.1
8qypc00390ffsdqhk0jswd8ia7nv48zb-curl-8.1.1
9agln788jc9gzpk28nhlppp2q8bj4744-curl-8.1.1
23k28h2cwwcz5k35qql6jf5ag1h8mc5l-curl-8.1.1
ajlczl5vikfxf1c606cjxxp0qw8kz8c1-curl-8.1.1
dqaylq6faab959nnvs6n666ajdlni91l-curl-8.1.2
f20h5zjlm5kjbf1r9g1ib28a07cqprxd-curl-8.1.1
jp723208rmb64jsjs0bx7ybi76g3w3fq-curl-8.1.1
kczx3v0mgxq99g4khjl78synll693q7m-curl-8.2.1

Reliable Updates

Reliable Updates

  • Isolated
  • Atomic
  • Revertible

Generations

 91   2023-08-14 08:18:24   
 92   2023-08-14 10:43:20   
 93   2023-08-15 07:55:07   
 94   2023-08-15 10:59:04   
 95   2023-08-15 11:09:31   
 96   2023-08-15 11:32:57   
 97   2023-08-15 15:40:17   
 98   2023-08-15 16:17:42   
 99   2023-08-16 08:03:20   
100   2023-08-16 09:25:32   (current)

Development environments

  • Synchronization between developers
  • Production == development environment

Example

{
  ...
  devShells.default = pkgs.mkShell {
    buildInputs = [
      (python.withPackages python-packages-devel)
      # python language server
      pkgs.nodePackages.pyright
      # cli tool to validate OpenAPI schemas
      pkgs-unstable.swagger-cli
    ];
  };
}

Minimal application containers

What Nix does differently

Traditional approach
  • Choose a base Linux distribution
  • Procedually install application
  • Save result as new image
The Nix way
  • Build application locally
  • Create image by copying required files

Advantages

  • No additional effort
  • Smaller size
  • Lower attack surface
  • Lower maintenance burden

Example

docker-img =
  pkgs.dockerTools.buildLayeredImage {
    name = wlo-topic-assistant.pname;
    tag = wlo-topic-assistant.version;
    config = {
      Cmd = [ "${wlo-topic-assistant}/bin/wlo-topic-assistant" ];
    };
  };
REPOSITORY                     SIZE
localhost/wlo-topics-py        8.33 GB
docker.io/library/python       918 MB
localhost/wlo-topic-assistant  1.87 GB

Layering

Creating layer 90 from paths: ['/nix/store/9kfyzpag05sh5db0l7cphq5n0714hg07-python3.10-click-8.1.3']
Creating layer 91 from paths: ['/nix/store/isrbd627p6ia29hvhsl8yjl2wlj51467-python3.10-courlan-0.9.2']
Creating layer 92 from paths: ['/nix/store/djvdzm9mf1rbj6f90jc6h3m4abvb0vcb-python3.10-faust-cchardet-2.1.18']
Creating layer 93 from paths: ['/nix/store/cjjqk88ms28px76fm2934s49bz65cqzy-python3.10-htmldate-1.4.3']
Creating layer 94 from paths: ['/nix/store/lrpsx72jxq5x8cm2rv726dlbwbxnjcmb-python3.10-joblib-1.2.0']
Creating layer 95 from paths: ['/nix/store/k98zixdmh9scs1n3bbkdcayy344pjsj8-python3.10-jusText-3.0.0']
Creating layer 96 from paths: ['/nix/store/2d6agr77bhn1fihawmh4j82h6a90hjhj-python3.10-py3langid-0.2.2']
Creating layer 97 from paths: ['/nix/store/h2svi571662bkp22psm25fcfxpmdw5xj-python3.10-pycurl-7.45.1']
Creating layer 98 from paths: ['/nix/store/sw5xn8lll2bwf1qkjr1c2i5565949nzb-python3.10-tqdm-4.64.1']
Creating layer 99 from paths: ['/nix/store/lna1hcni8icv0yn7r4bjlf8mhl5p4wi8-python3.10-brotlicffi-1.0.9.2', '/nix/store/8za3djyrmkwyqwxji9966hpm8fvvbkda-python3.10-charset-normalizer-3.0.1', '/nix/store/jil4qsy4kxg9gi25m5z30gcvbmap4jxm-python3.10-cherrypy-18.8.0', '/nix/store/d9ashjapgrzfslqqyis5w29f2gf8i6fv-python3.10-idna-3.4', '/nix/store/kplqgqlhpasiywak7d9n1lh422i932mq-python3.10-nltk-3.8.1', '/nix/store/qah442kmnkb68160wvl0rdr48m56gvg8-python3.10-pyphen-0.14.0', '/nix/store/jm1d558kssgvyc2qhsqcrl4dg4sc1b1c-python3.10-trafilatura-1.5.0', '/nix/store/s72kwrl6i1kh4s67sz5ra27cnljcnh3x-python3.10-requests-2.29.0', '/nix/store/ix4lmx4phl6y8s68948pia1y6qnlh7aq-python3.10-text-statistics-1.0.4', '/nix/store/cvvzcr89acfziii7p9xxajqjlvcybdak-python-kidra-1.1.0']
Creating layer 100 with customisation...

3. Disadvantages

  • Steep learning curve
  • Patchy documentation
  • Package isolation increases system size
  • Language-specific packages have to be "nixified"

Example: Python

{ lib
, buildPythonPackage
, fetchPypi
, six
}:
buildPythonPackage rec {
  pname = "treelib";
  version = "1.6.4";
  format = "setuptools";
  src = fetchPypi {
    inherit pname version;
    sha256 = "sha256-Gi6Dj2uZ4mkLw9mS1aHwTNtK9lZL12iIg8I9FyV7uyo=";
  };
  propagatedBuildInputs = [ six ];
  pythonImportsCheck = [ "treelib" ];
}

Nix-init

$ nix-init
Enter output path (defaults to current directory)
❯ pkgs
Enter url
❯ https://pypi.org/project/treelib/
Enter tag or revision (defaults to 1.6.4)
❯ 1.6.4
Enter version
❯ 1.6.4
Enter pname
❯ treelib
How should this package be built?
❯ 1 - buildPythonPackage - setuptools

Supported sources

builtins.fetchGit
fetchCrate
fetchFromBitbucket
fetchFromGitHub
fetchFromGitLab
fetchFromGitea
fetchFromGitiles
fetchFromRepoOrCz
fetchFromSourcehut
fetchHex
fetchPypi
fetchgit
fetchhg
fetchsvn

4. Why NixOS

Infrastructure as Code

  • No undocumented changes
  • Identical behavior between machines
  • Simple deployment

Reliability

  • Problematic configurations found at evaluation
  • Resilient against power outages
  • Revertible system state

5. Summary

Nix package manager
  • Focus on reproducibility & isolation
  • Integrates well with existing infrastructure (e.g. Docker ecosystem)
  • "Applications as Code"
NixOS
  • Nix, as an operating system
  • Can replace many existing technologies
  • Infrastructure as Code