Declarative User Package Management in NixOS

NixOS
NixOS

NixOS is a Linux distribution built around the nix package manager. The configuration of the entire system is In theory, NixOS is the easiest Linux distribution to use because of its declarative nature. In theory… allowing for immutability and reproducibility.

Let’s take a look at managing a user’s package environment declaratively in NixOS version 20.03. The The nix expression language is not particularly difficult. It’s the scope and flexibility of the abstraction layer that makes the initial NixOS learning experience painful. of NixOS dictates that there are numerous declarative methods. We can use attribute sets, build environments, or even community Home manager is a system that manages a user’s environment. It’s similar to the system’s configuration.nix setup. like home manager.

Declarative Package Management

We will combine the command nix-env with the user’s config.nix to achieve a declarative package setup. The user specific configuration is located at ~/.config/nixpkgs/config.nix. Create this file if it does not exist.

The user’s nixpkgs configuration config.nix places us in a context This may be familiar to those who configure the system from configuration.nix. to nixpkgs.config. Populate config.nix with multiple build environments and install packages using nix-env. This can be achieved more optimally with nix flake or you could use nix-shell for projects and temporary environments. The following is a basic config.nix that makes Beware of package name collisions that result in infinite recursion. Notice that Awesome (with a capital) is used instead of awesome in one of the overrides. of package overriding. Check out my config.nix here.

nix
{
  allowUnfree = true;

  packageOverrides = pkgs: with pkgs; {

    Awesome = pkgs.buildEnv {
      name = "awesome";
      paths = [
        awesome
        lxappearance
        paper-gtk-theme
      ];
    };

    Golang = pkgs.buildEnv {
      name = "golang";
      paths = [
        go
      ];
    };

    PHP = pkgs.buildEnv {
      name = "php";
      paths = [
        php74
      ];
    };

    C = pkgs.buildEnv {
      name = "c";
      paths = [
        gnumake
        meson
        ninja
        gcc
      ];
    };
  };
}

Install the above build sets in config.nix using nix-env -Air. The -A flag is for the attribute path, -i to install the packages and -r for removing all This is like running an apt install or pacman -Syu that intelligently erases all user packages before installing. packages.

shell
nix-env -Air nixos.Awesome nixos.Golang nixos.PHP nixos.C

Distribution independent installations of nix default to the nixpkgs Each package contains the prefix nixos. This is the default NixOS channel containing the packages. Run nix-channel --list to view the current user’s channel prefixes. as the default channel name.

shell
nix-env -Air nixpkgs.Awesome nixpkgs.Golang nixpkgs.PHP nixpkgs.C

Querying the installed packages with nix-env -q shows that there are four packages installed each containing the programs declared in config.nix.

shell
$ nix-env -q
awesome
c
golang
php

One could exploit the recursive aspect and group packages further by any metric. Below we add another set that groups the above declarations by machine.

nix
{
  allowUnfree = true;

  packageOverrides = pkgs: with pkgs; rec {

    Machine1 = pkgs.buildEnv {
      name = "machine1";
      paths = [ Awesome Golang PHP C ];
    };

    # Package declarations for Awesome, Golang, PHP, & C ...
  };
}

This should evaluate quicker than the first install command. and querying this package with nix-env shows that there is one package installed containing a program set of other program sets declared within config.nix.

shell
$ nix-env -Air nixos.Machine1
$ nix-env -q
machine1

This is immensely beneficial for use cases that prioritize keeping root system build times Faster configuration switches. Users can install packages independent of the system’s configuration.nix declaratively. The nix store also allows This gives the option of only exposing the binaries from /bin. exposing program paths to the user’s environment.

nix
{
  allowUnfree = true;

  packageOverrides = pkgs: with pkgs; {

    Awesome = pkgs.buildEnv {
      name = "awesome";
      paths = [
        awesome
        lxappearance
        paper-gtk-theme
      ];
      pathsToLink = [ "/etc" "/share" "/bin" ];
    };
  };
}

Instantly There’s actually no need to remove or uninstall packages in declarative package management. The main idea is to either rollback bad changes or delete and switch to newer or older generations. all packages with nix-env -Air by switching to an empty environment.

shell
nix-env -Air

List previous environment switches with --list-generations. You can further manipulate the environment lineage by using the generation arguments in the nix-env manual.

shell
$ nix-env --list-generations
368 2021-08-13 23:16:36
369 2021-08-22 00:01:38
370 2021-08-22 00:05:47
371 2021-08-22 00:23:03

Unstable Declarative Package Management

The above is nice and all, but what if a package is not in the nixos stable channel? We can add the The unstable channel is based on the master repository and contains more packages than the stable channel. channel from the system side, but what happens if its too unstable?

Since this is NixOS — we can mix and match. In config.nix bring in the unstable channel directly. We This is wonderful. Getting a quick and clean install of programs like Ungoogled Chromium isn’t so easy in other environments. ungoogled-chromium from the The paranoid user can set any package archive and pin specific versions. channel as an example.

nix
let
  unstable = import (builtins.fetchTarball "https://releases.nixos.org/nixos/unstable/nixos-21.03pre265961.891f607d530/nixexprs.tar.xz") {};

  # Or lock at a specific commit to stop moving targets.
  # unstable = import (builtins.fetchTarball {
  #  url = "https://releases.nixos.org/nixos/unstable/nixos-21.03pre265961.891f607d530/nixexprs.tar.xz";
  #  sha256 = "1hwwb4n15bbqxnbqffq4kfb369vz65sq74p537fqdp6i4ywpqsyh"; }) {};
in

{
  allowUnfree = true;

  packageOverrides = pkgs: with pkgs; {

    Awesome = pkgs.buildEnv {
      name = "awesome";
      paths = [
        awesome
        lxappearance
        paper-gtk-theme
        unstable.ungoogled-chromium
      ];
      pathsToLink = [ "/etc" "/share" "/bin" ];
    };
  };
}

Advanced Declarative Package Management

Here’s a scenario: You’re reading some Lua code and you need a lua formatting tool. It looks like LuaFormatter will do the job, but that package is neither in the stable or unstable channel. You install luarocks — the lua package manager, but something luaformatter attempts to compile its submodules from the read only nix path of luarocks. happens when it tries to install luaformatter. What now?

Create a This is similar to writing a PKGBUILD or APKBUILD. The ease of writing a derivation depends on upstream’s build and install complexity. for luaformatter that config.nix can call and build to expose the package to the user’s environment. Create a packages directory and a folder containing default.nix at ~/.config/nixpkgs/packages/luaformatter.

nix
{ lib, stdenv, fetchFromGitHub, cmake }:

stdenv.mkDerivation rec {
  pname = "LuaFormatter";
  version = "1.3.3";

  src = fetchFromGitHub {
    sha256 = "1dfqsh6v8brnwzg3lgi7228lw08qqfy4ghbjyvwn7mr82fy1xcnd";
    rev = version;
    repo = pname;
    owner = "Koihik";
    fetchSubmodules = true;
  };

  buildInputs = [ cmake ];

  meta = with lib; {
    inherit (src.meta) homepage;
    description = "Code formatter for Lua";
    license = licenses.asl20;
    platforms = platforms.linux;
  };
}

Test the derivation using nix-build. Run There is nothing special about this expression. The default.nix here is in the correct form for callPackage to understand. A derivation can contain anything — though I prefer a default.nix to always return a package, a shell.nix to return a shell, a module.nix a module, a flake.nix a flake, and so on. nix This is how the nixpkgs repository calls most derivations. This format sets you up for a pull request if that’s your thing. with the --expr argument inside the folder containing default.nix.

shell
nix-build -E 'with import <nixpkgs> {}; callPackage ./default.nix {}'

Use the --keep-failed (-K) argument to save the temporary build folder on failure. This allows testing fixups in the failed build environment.

shell
nix-build -K -E 'with import <nixpkgs> {}; callPackage ./default.nix {}'

Call the derivation if the build succeeds with pkgs.callPackage from your config.nix

nix
let
  unstable = import (builtins.fetchTarball "https://releases.nixos.org/nixos/unstable/nixos-21.03pre265961.891f607d530/nixexprs.tar.xz") {};
in {
  allowUnfree = true;

  packageOverrides = pkgs: with pkgs; {

    Awesome = pkgs.buildEnv {
      name = "awesome";
      paths = [
        awesome
        lxappearance
        paper-gtk-theme
        unstable.ungoogled-chromium
        (callPackage ./packages/luaformatter/default.nix {})
      ];
      pathsToLink = [ "/etc" "/share" "/bin" ];
    };
  };
}

Think of a derivation as an abstraction of the typical application build process. In the above derivation we imported stdenv — the standard environment that contains common tools and dependencies most programs need. The fetchFromGitHub Or rather an attribute set containing attributes. contains methods that extend the standard environment allowing us to pull in repositories the Nix builds in a sand–boxed environment by default, so arbitrary network connections are not allowed outside the scope of attributes. way. The application luaformatter requires cmake, so it is sourced as one of the buildInputs — another attribute within the derivation context.

A derivation can be extended in multiple ways, allowing us to Most applications and libraries are already built for us. build any application. The nixpkgs repository has an overview of building artifacts in popular environments like Python. In tricky application builds, step down a level of abstraction to fix it up using the derivation’s phase attributes.

Finally a derivation can be paired with a module allowing us to hide away the above work into a simple Writing a module is a story for another day. of attributes that a user can easily understand. This is what most users care about. The following is a made up example from the simple derivation above.

nix
{
  programs.awesome.enable = true;
  programs.awesome.luaFormat = true;

  # Or

  programs.awesome = {
    enable = true;
    luaFormat = true;
  };
}
27 May 2020 — Written
20 February 2022 — Updated
Thedro Neely — Creator
declarative-user-package-management-in-nixos.md — Article

More Content

Openring

Web Ring

Comments

References

  1. https://thedroneely.com/git/
  2. https://thedroneely.com/
  3. https://thedroneely.com/posts/
  4. https://thedroneely.com/projects/
  5. https://thedroneely.com/about/
  6. https://thedroneely.com/contact/
  7. https://thedroneely.com/abstracts/
  8. https://ko-fi.com/thedroneely
  9. https://thedroneely.com/tags/nix/
  10. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#isso-thread
  11. https://thedroneely.com/posts/rss.xml
  12. https://thedroneely.com/images/declarative-user-package-management-in-nixos.png
  13. https://nixos.org/
  14. https://github.com/nix-community/home-manager#home-manager-using-nix
  15. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#declarative-package-management
  16. https://nixos.org/nixpkgs/manual#sec-declarative-package-management
  17. https://nixos.org/manual/nix/unstable/command-ref/nix-env.html#description
  18. https://nixos.org/nixpkgs/manual#chap-packageconfig
  19. https://search.nixos.org/packages
  20. https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#flake-format
  21. https://nixos.org/manual/nix/stable/command-ref/nix-shell.html#description
  22. https://nixos.org/guides/nix-pills/nixpkgs-overriding-packages.html#idm140737319627440
  23. https://www.thedroneely.com/git/thedroneely/dotfiles/tree/.config/nixpkgs
  24. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-8619021
  25. https://nixos.wiki/index.php?title=Cheatsheet&useskin=vector
  26. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-6fd9c82
  27. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-d728370
  28. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-536d8b8
  29. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-66aa178
  30. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-eb20d8c
  31. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-4c65130
  32. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-c6f5637
  33. https://nixos.org/manual/nix/unstable/command-ref/nix-env.html#operation---list-generations
  34. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-11ba472
  35. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#unstable-declarative-package-management
  36. https://github.com/NixOS/nixpkgs
  37. https://github.com/Eloston/ungoogled-chromium#ungoogled-chromium
  38. https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
  39. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-29aea20
  40. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#advanced-declarative-package-management
  41. https://www.lua.org/
  42. https://github.com/Koihik/LuaFormatter#readme
  43. https://luarocks.org/
  44. https://en.wikipedia.org/wiki/Upstream_%28software_development%29
  45. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-569ecc7
  46. https://github.com/NixOS/nixpkgs/blob/f7ad47a8f62e28e840e0f11177b2f7d6a16d29b2/pkgs/top-level/agda-packages.nix#L22
  47. https://github.com/NixOS/nixpkgs/pulls
  48. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-06693af
  49. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-a9f95ce
  50. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-9d8b7de
  51. https://github.com/NixOS/nixpkgs/tree/master/doc/languages-frameworks
  52. https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/python.section.md
  53. https://nixos.org/nixpkgs/manual#sec-stdenv-phases
  54. https://nixos.wiki/wiki/Module
  55. https://thedroneely.com/posts/writing-nixos-modules-and-switching-to-cgit/
  56. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#code-block-2951679
  57. https://www.thedroneely.com/posts/declarative-user-package-management-in-nixos.md
  58. https://thedroneely.com/posts/a-few-links/
  59. https://thedroneely.com/posts/running-nixos-linux-containers/
  60. https://thedroneely.com/archives/tags/
  61. https://git.sr.ht/~sircmpwn/openring
  62. https://drewdevault.com/2022/11/12/In-praise-of-Plan-9.html
  63. https://drewdevault.com/
  64. https://mxb.dev/blog/the-indieweb-for-everyone/
  65. https://mxb.dev/
  66. https://www.taniarascia.com/simplifying-drag-and-drop/
  67. https://www.taniarascia.com/
  68. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#isso-thread
  69. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#declarative-package-management
  70. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-8619021
  71. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-6fd9c82
  72. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-d728370
  73. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-536d8b8
  74. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-66aa178
  75. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-eb20d8c
  76. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-4c65130
  77. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-c6f5637
  78. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-11ba472
  79. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#unstable-declarative-package-management
  80. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-29aea20
  81. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#advanced-declarative-package-management
  82. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-569ecc7
  83. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-06693af
  84. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-a9f95ce
  85. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-9d8b7de
  86. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#code-block-2951679
  87. https://thedroneely.com/posts/my-sublime-text-setup/
  88. https://thedroneely.com/posts/tailwind-css-and-beyond/
  89. https://thedroneely.com/posts/nixos-in-the-wild/
  90. https://thedroneely.com/posts/typesetting-in-latex/
  91. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/
  92. https://thedroneely.com/posts/tweaking-goaccess-for-analytics/
  93. https://drewdevault.com/2022/09/16/Open-source-matters.html
  94. https://mxb.dev/blog/make-free-stuff/
  95. https://thedroneely.com/sitemap.xml
  96. https://thedroneely.com/index.json
  97. https://thedroneely.com/resume/
  98. https://gitlab.com/tdro
  99. https://github.com/tdro
  100. https://codeberg.org/tdro
  101. https://thedroneely.com/analytics
  102. https://thedroneely.com/posts/declarative-user-package-management-in-nixos#
  103. https://creativecommons.org/licenses/by-sa/2.0/
  104. https://thedroneely.com/git/thedroneely/thedroneely.com
  105. https://opensource.org/licenses/GPL-3.0
  106. https://www.thedroneely.com/
  107. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/#