Daniel Siepmann - Coding is Art

Blog Post

Nix Journey Part 2

Published: , Updated:

Topics: nix

Introduction

I'll dig into the benefits I see in source based distributions in general and especially Nix. This is not about my setup or what I've learned, but what I like right now about my existing setup.

We dig into the nixpkgs repository, the nix language and features we get by using the language to describe the system. We will also see how the NixOS and home-manager modules allowed me to learn Linux way easier.

There are a couple of more features, but they are not important to me right now. You might want to check https://nixos.org/explore.html and https://nix.dev/tutorials/#tutorials to see more benefits not covered by this post.

Benefits of Nixpkgs

Nixpkgs is a huge repository containing Nix expressions for various software packages to be built. It should be similar to AUR, NUR and Gentoo as well as other source based distributions. I only know Gentoo from one of my previous companies where everything was managed for me.

You can find the repository at https://github.com/NixOS/nixpkgs/, and it is huge, it covers 101,384 packages while writing this post, accordingly to https://repology.org/repository/nix_unstable. While some packages exist in multiple configurations, e.g. ffmpeg_6-headless, ffmpeg_6-full, ffmpeg_6, …

So what are the actual benefits?

I personally like that this provides value to people not even using the package manager. It is a huge library of documentation on how to build software. It is open source and people can read the source code based documentation. People can search the repository for packages based on a filesystem level.

Packages are grouped into folders based on some categories. That makes them discoverable. You are tired of your text editor? Check out the category and discover new unknown editors: https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/editors.

Each package also has some meta info with the URL of the project, a description, etc. That allows to grep the files by keywords and return the URLs. That allows to check out different software even easier.

Benefits of Nix language

I really like to have an actual programming language at hand. That opens the opportunity to do whatever I want. The PHP ecosystem has the same shift right now. Some projects like Symfony seem to switch from YAML (after switching there from XML) to PHP. It doesn't limit the user, the user doesn't need to learn another format which most probably is not suited for the purpose anyway as it was developed for a different use case.

Benefits of Nix language - string interpolation

I'll try to demonstrate the huge benefit with a specific use case taken from my current setup. Say you are using Neovim as your preferred text editor for coding. You mostly code PHP code and want to add a language server for better support. I'll install Phpactor (https://phpactor.readthedocs.io/). But I won't follow the official instructions and install it globally on my system, via another package manager specific for PHP. Instead, I'll use Nix the language to add it via my home-manager configuration. I don't want it to be globally installed and pollute my system. I only need it from within Neovim my editor.

home-manager comes with a module to configure Neovim, allowing to adding and configuring plugins via Nix the language. Neovim itself has an official plugin called lspconfig easing the configuration of language servers, I'll install and configure that plugin to integrate Phpactor, the corresponding configuration looks like this:

{
    plugin = nvim-lspconfig;
    type = "lua";
    config = ''
      require('lspconfig').phpactor.setup({
        cmd = {
          '${pkgs.phpactor}/bin/phpactor',
          'language-server',
        },
      })
    '';
  }

That way I add the plugin nvim-lspconfig with its configuration, which is Lua instead of vim script. Thanks to being in Nix, I can use string interpolation. That way I can reference the Phpactor package and a specific file. That will evaluated to the final installation path of the package and will install the package there. It won't add the package to my user packages, just ensure it is installed at the path and replace the variable with the path. The final result within build Neovim configuration looks like this:

require('lspconfig').phpactor.setup({
  cmd = {
    '/nix/store/j138qfvvq2dsz1br3rp2mdcyq709690v-phpactor-2023.06.17/bin/phpactor',
    'language-server',
  },
})

That way Neovim calls the package, but the package won't be added to my PATH or somewhere else. No other software knows the package exists. That's only one of the many benefits by having Nix as language to describe the system.

Benefits of Nix language - one language

Another benefit of Nix language is that you don't need to know or learn multiple languages or formats. Git for example is configured using a INI format. You could learn that format and configure Git yourself. Or you can use the Nix language and home-manager module to configure Git. You can see my current full Git configuration here: https://git.daniel-siepmann.de/danielsiepmann/nixpkgs/src/branch/main/home/programs/git.nix and further configurations for various programs here: https://git.daniel-siepmann.de/danielsiepmann/nixpkgs/src/branch/main/home/programs.

{ pkgs }:

{
  enable = true;

  userName = "Daniel Siepmann";
  userEmail = "coding@daniel-siepmann.de";

  signing = {
    key = "15560EF4";
    signByDefault = true;
  };

  aliases = {
    c = "checkout";
    ss = "show -s";
    # …
  };

  ignores = [
    # Some files which might be generated by system or tools
    "*~"
    "*.swp"
    # …
  ];

  diff-so-fancy = {
    enable = true;
    markEmptyLines = false;
    stripLeadingSymbols = false;
    useUnicodeRuler = false;
  };

  extraConfig = {
    advice = {
      detachedHead = false;
    };

    branch = {
      autosetuprebase = "always";
    };
    # …
  };
}

Nixpkgs adds some generators to convert Nix into various formats. Check out https://nixos.org/manual/nixpkgs/unstable/#sec-generators and https://github.com/NixOS/nixpkgs/blob/master/lib/generators.nix.

Benefits of Modules

Nix doesn't have modules. Neither the language nor the package manager have modules built-in. NixOS and home-manager (as well as other software solutions build on top) provide modules, all working the same way. And we already touched the modules twice in above sections. Modules are explained at https://nixos.wiki/wiki/NixOS_modules and https://nixos.org/manual/nixos/unstable/index.html#sec-writing-modules.

Modules consist of the following structure:

{
  options = {
    # option declarations
  };
  config = {
    # option definitions
  };
}

They first define available options, those can be used to describe your system. They auto generate their own man pages and websites. They come with defaults, description and types. Adding a none allowed value will result in an error message and none build system configuration, instead of a broken system. An example of an option looks like this:

{
  options = {
    programs.git = {
      aliases = mkOption {
        type = types.attrsOf types.str;
        default = { };
        example = { co = "checkout"; };
        description = "Git aliases to define.";
      };
    };
  };
};

This describes the option aliases within namespace programs.git. We already saw the usage in above example of my Git configuration.

The other building block is the config, which actually does something based on the given option values. It can look like this:

{
  config = mkIf cfg.enable (mkMerge [
    (mkIf cfg.diff-so-fancy.enable {
      home.packages = [ pkgs.diff-so-fancy ];
      programs.git.iniContent =
        let dsfCommand = "${pkgs.diff-so-fancy}/bin/diff-so-fancy";
        in {
          core.pager = "${dsfCommand} | ${pkgs.less}/bin/less ${
              escapeShellArgs cfg.diff-so-fancy.pagerOpts
            }";
          interactive.diffFilter = "${dsfCommand} --patch";
          diff-so-fancy = {
            markEmptyLines = cfg.diff-so-fancy.markEmptyLines;
            changeHunkIndicators = cfg.diff-so-fancy.changeHunkIndicators;
            stripLeadingSymbols = cfg.diff-so-fancy.stripLeadingSymbols;
            useUnicodeRuler = cfg.diff-so-fancy.useUnicodeRuler;
            rulerWidth = mkIf (cfg.diff-so-fancy.rulerWidth != null)
              (cfg.diff-so-fancy.rulerWidth);
          };
        };
    })
  ]);
}

This will check whether the option diff-so-fancy.enable within programs.git is set to true. It then will add the corresponding configuration to the ini file exposing a properly set up diff-so-fancy.

The great benefits are:

  • Discoverability. I can check the available options from the modules to learn something new.
  • Type safeness. The configured values will be checked prior building the system configuration.
  • Documentation. Each option is documented, what values are expected, what's the default, what is the option supposed to do?
  • Manual. The config section is a manual explaining what will happen to the system and how. I could use that again if I wouldn't use Nix and home-manager in order to know how to add and configure diff-so-fancy by hand.

Concrete learning example

I had issues configuring the same pointer and theme by hand throughout my Ubuntu system. Some applications shown a different cursor. It took me some time. The commit https://git.daniel-siepmann.de/danielsiepmann/nixpkgs/commit/3283c5e1dc961d8825d64f215ded3c7e7afc336c demonstrates the benefit. I first fixed everything myself by configuring xresources. Then discovered that there are ready to use modules I could have used. Checking out the code of the module revealed it does exactly the same, so my solution wasn't that bad. I could have looked for the module earlier and not waste resourcing myself. The documentation was already there, as code within a module.

Acknowledgements

I hope you found this blog post helpful. It should have revealed some great benefits of using Nix, Nixpkgs, home-manager and its modules to you. Thanks once more to @array@fosstodon.org and his thread on the Fediverse that motivated me to share my thoughts.

Further reading

Official sources:

3rd Party sources: