Preservation

Nix tooling to enable declarative management of non-volatile system state.

Inspired and heavily influenced by impermanence but not meant to be a drop-in replacement.

Documentation

Docs are available at https://nix-community.github.io/preservation

Prerequisites

Requires at least nixos-24.11

Why?

This aims to provide a declarative state management solution for NixOS systems without resorting to interpreters to do the heavy lifting. This should enable impermanence-like state management on an "interpreter-less" NixOS system.

Related:

License

This project is released under the terms of the MIT License. See LICENSE.

preservation.enable

Whether to enable the preservation module.

Type: boolean

Default: false

Example: true

Declared by:

preservation.preserveAt

Specify a set of locations and the corresponding state that should be preserved there.

Type: attribute set of (submodule)

Default: { }

Example:

{
  "/state" = {
    directories = [ "/var/lib/someservice" ];
    files = [
      {
        file = "/etc/wpa_supplicant.conf";
        how = "symlink";
      }
      {
        file = "/etc/machine-id";
        inInitrd = true;
      }
    ];
    users = {
      alice.directories = [ ".rabbit_hole" ];
      butz = {
        files = [
          {
            file = ".config/foo";
            mode = "0600";
          }
          "bar"
        ];
        directories = [ "unshaved_yaks" ];
      };
    };
  };
}

Declared by:

preservation.preserveAt.<name>.directories

Specify a list of directories that should be preserved. The paths are interpreted as absolute paths.

Type: list of ((submodule) or string convertible to it)

Default: [ ]

Example:

[
  "/var/lib/someservice"
]

Declared by:

preservation.preserveAt.<name>.directories.*.configureParent

Specify whether the parent directory of this directory shall be configured with custom ownership and permissions.

By default, missing parent directories are always created with ownership root:root and mode 0755, as described in tmpfiles.d(5).

Ownership and mode may be configured through the options parent.user, parent.group, parent.mode.

Defaults to true when how is set to symlink and user is not root.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.directories.*.createLinkTarget

Only used when how is set to symlink.

Specify whether to create an empty directory with the specified ownership and permissions as target of the symlink.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.directories.*.directory

Specify the path to the directory that should be preserved.

Type: string

Declared by:

preservation.preserveAt.<name>.directories.*.group

Specify the group that owns the directory.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.directories.*.how

Specify how this directory should be preserved.

Type: one of “bindmount”, “symlink”

Default: "bindmount"

Declared by:

preservation.preserveAt.<name>.directories.*.inInitrd

Whether to prepare preservation of this directory in initrd.

Note: For most directories there is no need to enable this option.

Important: Note that both owner and group for this directory need to be available in the initrd for permissions to be set correctly.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.directories.*.mode

Specify the access mode of the directory. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.directories.*.mountOptions

Specify a list of mount options that should be used for this directory. These options are only used when how is set to bindmount. By default, bind and X-fstrim.notrim are added, use mkForce to override these if needed. See also fstrim(8).

Type: list of ((submodule) or string convertible to it)

Declared by:

preservation.preserveAt.<name>.directories.*.mountOptions.*.name

Specify the name of the mount option.

Type: string

Example: "bind"

Declared by:

preservation.preserveAt.<name>.directories.*.mountOptions.*.value

Optionally specify a value for the mount option.

Type: null or string

Default: null

Declared by:

preservation.preserveAt.<name>.directories.*.parent.group

Specify the group that owns the parent directory of this file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.directories.*.parent.mode

Specify the access mode of the parent directory of this file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.directories.*.parent.user

Specify the user that owns the parent directory of this file.

Type: string

Default: "root"

Declared by:

preservation.preserveAt.<name>.directories.*.user

Specify the user that owns the directory.

Type: string

Default: "root"

Declared by:

preservation.preserveAt.<name>.files

Specify a list of files that should be preserved. The paths are interpreted as absolute paths.

Type: list of ((submodule) or string convertible to it)

Default: [ ]

Example:

[
  {
    file = "/etc/wpa_supplicant.conf";
    how = "symlink";
  }
  {
    file = "/etc/machine-id";
    inInitrd = true;
  }
]

Declared by:

preservation.preserveAt.<name>.files.*.configureParent

Specify whether the parent directory of this file shall be configured with custom ownership and permissions.

By default, missing parent directories are always created with ownership root:root and mode 0755, as described in tmpfiles.d(5).

Ownership and mode may be configured through the options parent.user, parent.group, parent.mode.

Defaults to true when how is set to symlink and user is not root.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.files.*.createLinkTarget

Only used when how is set to symlink.

Specify whether to create an empty file with the specified ownership and permissions as target of the symlink.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.files.*.file

Specify the path to the file that should be preserved.

Type: string

Declared by:

preservation.preserveAt.<name>.files.*.group

Specify the group that owns the file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.files.*.how

Specify how this file should be preserved:

  1. Either a file is placed both on the volatile and on the persistent volume, with a bind mount from the former to the latter.

  2. Or a symlink is created on the volatile volume, pointing to the corresponding location on the persistent volume.

Type: one of “bindmount”, “symlink”

Default: "bindmount"

Declared by:

preservation.preserveAt.<name>.files.*.inInitrd

Whether to prepare preservation of this file in the initrd.

Note: For most files there is no need to enable this option.

/etc/machine-id is an exception because it needs to be populated/read very early.

Important: Note that both owner and group for this file need to be available in the initrd for permissions to be set correctly.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.files.*.mode

Specify the access mode of the file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0644"

Declared by:

preservation.preserveAt.<name>.files.*.mountOptions

Specify a list of mount options that should be used for this file. These options are only used when how is set to bindmount. By default, bind is added, use mkForce to override this if needed.

Type: list of ((submodule) or string convertible to it)

Declared by:

preservation.preserveAt.<name>.files.*.mountOptions.*.name

Specify the name of the mount option.

Type: string

Example: "bind"

Declared by:

preservation.preserveAt.<name>.files.*.mountOptions.*.value

Optionally specify a value for the mount option.

Type: null or string

Default: null

Declared by:

preservation.preserveAt.<name>.files.*.parent.group

Specify the group that owns the parent directory of this file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.files.*.parent.mode

Specify the access mode of the parent directory of this file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.files.*.parent.user

Specify the user that owns the parent directory of this file.

Type: string

Default: "root"

Declared by:

preservation.preserveAt.<name>.files.*.user

Specify the user that owns the file.

Type: string

Default: "root"

Declared by:

preservation.preserveAt.<name>.persistentStoragePath

Specify the location at which the directories, files, users.directories and users.files should be preserved. Defaults to the name of the parent attribute set.

Type: path

Default: "‹name›"

Declared by:

preservation.preserveAt.<name>.users

Specify a set of users with corresponding files and directories that should be preserved.

Type: attribute set of (submodule)

Default: { }

Example:

{
  alice.directories = [ ".rabbit_hole" ];
  butz = {
    files = [
      {
        file = ".config/foo";
        mode = "0600";
      }
      "bar"
    ];
    directories = [ "unshaved_yaks" ];
  };
}

Declared by:

preservation.preserveAt.<name>.users.<name>.directories

Specify a list of directories that should be preserved for this user. The paths are interpreted relative to home.

Type: list of ((submodule) or string convertible to it)

Default: [ ]

Example:

[
  ".rabbit_hole"
]

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.configureParent

Specify whether the parent directory of this directory shall be configured with custom ownership and permissions.

By default, missing parent directories are always created with ownership root:root and mode 0755, as described in tmpfiles.d(5).

Ownership and mode may be configured through the options parent.user, parent.group, parent.mode.

Defaults to true when how is set to symlink and user is not root.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.createLinkTarget

Only used when how is set to symlink.

Specify whether to create an empty directory with the specified ownership and permissions as target of the symlink.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.directory

Specify the path to the directory that should be preserved.

Type: string

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.group

Specify the group that owns the directory.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.how

Specify how this directory should be preserved.

Type: one of “bindmount”, “symlink”

Default: "bindmount"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.inInitrd

Whether to prepare preservation of this directory in initrd.

Note: For most directories there is no need to enable this option.

Important: Note that both owner and group for this directory need to be available in the initrd for permissions to be set correctly.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.mode

Specify the access mode of the directory. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.mountOptions

Specify a list of mount options that should be used for this directory. These options are only used when how is set to bindmount. By default, bind and X-fstrim.notrim are added, use mkForce to override these if needed. See also fstrim(8).

Type: list of ((submodule) or string convertible to it)

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.mountOptions.*.name

Specify the name of the mount option.

Type: string

Example: "bind"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.mountOptions.*.value

Optionally specify a value for the mount option.

Type: null or string

Default: null

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.parent.group

Specify the group that owns the parent directory of this file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.parent.mode

Specify the access mode of the parent directory of this file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.parent.user

Specify the user that owns the parent directory of this file.

Type: string

Default: "‹user›"

Declared by:

preservation.preserveAt.<name>.users.<name>.directories.*.user

Specify the user that owns the directory.

Type: string

Default: "‹user›"

Declared by:

preservation.preserveAt.<name>.users.<name>.files

Specify a list of files that should be preserved for this user. The paths are interpreted relative to home.

Type: list of ((submodule) or string convertible to it)

Default: [ ]

Example:

[
  {
    file = ".config/foo";
    mode = "0600";
  }
  "bar"
]

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.configureParent

Specify whether the parent directory of this file shall be configured with custom ownership and permissions.

By default, missing parent directories are always created with ownership root:root and mode 0755, as described in tmpfiles.d(5).

Ownership and mode may be configured through the options parent.user, parent.group, parent.mode.

Defaults to true when how is set to symlink and user is not root.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.createLinkTarget

Only used when how is set to symlink.

Specify whether to create an empty file with the specified ownership and permissions as target of the symlink.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.file

Specify the path to the file that should be preserved.

Type: string

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.group

Specify the group that owns the file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.how

Specify how this file should be preserved:

  1. Either a file is placed both on the volatile and on the persistent volume, with a bind mount from the former to the latter.

  2. Or a symlink is created on the volatile volume, pointing to the corresponding location on the persistent volume.

Type: one of “bindmount”, “symlink”

Default: "bindmount"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.inInitrd

Whether to prepare preservation of this file in the initrd.

Note: For most files there is no need to enable this option.

/etc/machine-id is an exception because it needs to be populated/read very early.

Important: Note that both owner and group for this file need to be available in the initrd for permissions to be set correctly.

Type: boolean

Default: false

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.mode

Specify the access mode of the file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0644"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.mountOptions

Specify a list of mount options that should be used for this file. These options are only used when how is set to bindmount. By default, bind is added, use mkForce to override this if needed.

Type: list of ((submodule) or string convertible to it)

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.mountOptions.*.name

Specify the name of the mount option.

Type: string

Example: "bind"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.mountOptions.*.value

Optionally specify a value for the mount option.

Type: null or string

Default: null

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.parent.group

Specify the group that owns the parent directory of this file.

Type: string

Default: "config.users.users.\${defaultOwner}.group"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.parent.mode

Specify the access mode of the parent directory of this file. See the section Mode in tmpfiles.d(5) for more information.

Type: string

Default: "0755"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.parent.user

Specify the user that owns the parent directory of this file.

Type: string

Default: "‹user›"

Declared by:

preservation.preserveAt.<name>.users.<name>.files.*.user

Specify the user that owns the file.

Type: string

Default: "‹user›"

Declared by:

preservation.preserveAt.<name>.users.<name>.home

Specify the path to the user’s home directory.

Type: path, not containing newlines or colons

Default: "config.users.users.\${name}.home"

Declared by:

preservation.preserveAt.<name>.users.<name>.username

Specify the user for which the directories and files should be persisted. Defaults to the name of the parent attribute set.

Type: string, not containing newlines or colons

Default: "‹user›"

Declared by:

Examples

See Configuration Options for all available options.

Simple

# configuration.nix
{
  config,
  lib,
  pkgs,
  ...
}:
{
  preservation = {
    enable = true;
    preserveAt."/persistent" = {
      files = [
        # auto-generated machine ID
        { file = "/etc/machine-id"; inInitrd = true; }
      ];
      directories = [
        "/var/lib/systemd/timers"
        # NixOS user state
        "/var/lib/nixos"
        "/var/log"
      ];
    };
  };

  # systemd-machine-id-commit.service would fail, but it is not relevant
  # in this specific setup for a persistent machine-id so we disable it
  #
  # see the firstboot example below for an alternative approach
  systemd.suppressedSystemUnits = [ "systemd-machine-id-commit.service" ];

}

Compatibility with systemd's ConditionFirstBoot

In this example the machine-id is preserved on the persistent volume via symlink instead of a bind-mount. The option configureParent causes the parent directory of the symlink's target, i.e. /persistent/etc/ to be created. Additionally, systemd-machine-id-commit.service is adapted to persist the tmpfs mount created by system to the persistent volume.

# configuration.nix
{
  config,
  lib,
  pkgs,
  ...
}:
{
  preservation = {
    enable = true;
    preserveAt."/persistent" = {
      files = [
        # auto-generated machine ID
        { file = "/etc/machine-id"; inInitrd = true; how = "symlink"; configureParent = true; }
        # ...
      ];
      directories = [
        # ...
      ];
    };
  };

  # systemd-machine-id-commit.service would fail, but it is not relevant
  # in this specific setup for a persistent machine-id so we disable it
  #
  # see the firstboot example below for an alternative approach
  systemd.suppressedSystemUnits = [ "systemd-machine-id-commit.service" ];

  # let the service commit the transient ID to the persistent volume
  systemd.services.systemd-machine-id-commit = {
    unitConfig.ConditionPathIsMountPoint = [
      ""
      "/persistent/etc/machine-id"
    ];
    serviceConfig.ExecStart = [
      ""
      "systemd-machine-id-setup --commit --root /persistent"
    ];
  };
}

Complex

# configuration.nix
{
  config,
  lib,
  pkgs,
  ...
}:
{
  preservation = {
    # the module doesn't do anything unless it is enabled
    enable = true;

    preserveAt."/persistent" = {

      # preserve system directories
      directories = [
        "/etc/secureboot"
        "/var/lib/bluetooth"
        "/var/lib/fprint"
        "/var/lib/fwupd"
        "/var/lib/libvirt"
        "/var/lib/power-profiles-daemon"
        "/var/lib/systemd/coredump"
        "/var/lib/systemd/rfkill"
        "/var/lib/systemd/timers"
        "/var/log"
        { directory = "/var/lib/nixos"; inInitrd = true; }
      ];

      # preserve system files
      files = [
        { file = "/etc/machine-id"; inInitrd = true; }
        { file = "/etc/ssh/ssh_host_rsa_key"; how = "symlink"; configureParent = true; }
        { file = "/etc/ssh/ssh_host_ed25519_key"; how = "symlink"; configureParent = true; }
        "/var/lib/usbguard/rules.conf"

        # creates a symlink on the volatile root
        # creates an empty directory on the persistent volume, i.e. /persistent/var/lib/systemd
        # does not create an empty file at the symlink's target (would require `createLinkTarget = true`)
        { file = "/var/lib/systemd/random-seed"; how = "symlink"; inInitrd = true; configureParent = true; }
      ];

      # preserve user-specific files, implies ownership
      users = {
        butz = {
          directories = [
            { directory = ".ssh"; mode = "0700"; }
            ".config/syncthing"
            ".config/Element"
            ".local/state/nvim"
            ".local/state/wireplumber"
            ".local/share/direnv"
            ".local/state/nix"
            ".mozilla"
          ];
          files = [
            ".histfile"
          ];
        };
        root = {
          # specify user home when it is not `/home/${user}`
          home = "/root";
          directories = [
            { directory = ".ssh"; mode = "0700"; }
          ];
        };
      };
    };
  };

  # Create some directories with custom permissions.
  #
  # In this configuration the path `/home/butz/.local` is not an immediate parent
  # of any persisted file, so it would be created with the systemd-tmpfiles default
  # ownership `root:root` and mode `0755`. This would mean that the user `butz`
  # could not create other files or directories inside `/home/butz/.local`.
  #
  # Therefore systemd-tmpfiles is used to prepare such directories with
  # appropriate permissions.
  #
  # Note that immediate parent directories of persisted files can also be
  # configured with ownership and permissions from the `parent` settings if
  # `configureParent = true` is set for the file.
  systemd.tmpfiles.settings.preservation = {
    "/home/butz/.config".d = { user = "butz"; group = "users"; mode = "0755"; };
    "/home/butz/.local".d = { user = "butz"; group = "users"; mode = "0755"; };
    "/home/butz/.local/share".d = { user = "butz"; group = "users"; mode = "0755"; };
    "/home/butz/.local/state".d = { user = "butz"; group = "users"; mode = "0755"; };
  };

}

Impermanence

Impermanence is the established solution for managing persistent state on NixOS systems. Preservation is inspired and heavily influenced by impermanence.

See Comparison for a high-level overview of their differences.

How does Preservation compare to impermanence

Preservation does not attempt to be a very generic solution

Preservation tries to fill a specific niche. For instance, Preservation does not support non-NixOS systems via home-manager, which is supported by impermanence. See Migration for more technical details.

Preservation only generates static configuration

That is configuration for systemd-tmpfiles and systemd mount units. This makes Preservation a potential candidate for state management on interpreter-less NixOS systems.

Impermanence makes use of NixOS activation scripts and custom systemd services with bash (at the point of writing this), to create files and directories, setup mounts and configure ownership and permissions (see next point).

Preservation must be precisely configured

There is no special runtime logic in place. This means that the user must define:

  • when the preservation should be set up: either in the initrd, or after (the default)
  • how the preservation should be set up: either by symlink, or bindmount (the default)
  • whether or not parent directories of the persisted files require special permissions

See Migration for specifics that need to be considered when coming from an impermanence setup.

Similar configuration

Preservation's configuration is based on, and very similar to that of impermanence. See Migration for technical details.

Global enable option

Preservation uses a global enable option, impermanence does not.

For thoughts on the enable option, see the discussion at https://github.com/nix-community/impermanence/pull/171 and for available configuration options see Configuration Options.

Migration from impermanence to Preservation

This section lists individual differences between impermanence and Preservation, to better understand them in context of a complete configuration Examples may be helpful.

The following points need to be considered when migrating an existing impermanence configuration to Preservation:

Global enable switch

The module must be explicitly enabled by setting preservation.enable to true.

When to persist

Files and directories that need to be persisted early, must be explicitly configured. For example /etc/machine-id:

This file needs to be persisted very early, by explicitly setting inInitrd to true:

preservation.preserveAt."/persistent".files = [
  { file = "/etc/machine-id"; inInitrd = true; }
];

How to persist

The mode of preservation must be set explicitly for some files and directories. This can be done by setting how to either symlink or bindmount (default). For most cases the default is sufficient but sometimes a symlink may be needed, for example /var/lib/systemd/random-seed.

This file is expected to not exist before it is initialized. A symlink can be used to cause its creation to happen on the persistent volume:

preservation.preserveAt."/persistent".files = [
  {
    file = "/var/lib/systemd/random-seed";
    # create a symlink on the volatile volume
    how = "symlink";
    # prepare the preservation early during startup
    inInitrd = true;
  }
];

Note that no file is created at the symlink's target, unless createLinkTarget is set to true.

Configuration of intermediate path components

Preservation does not handle any files or directories other than those specifically configured to be preserved, and optionally their immediate parent directories (via configureParent and the parent options).

All missing components of a preserved path that do not already exist, are created by systemd-tmpfiles with default ownership root:root and mode 0755.

Should such directories require different ownership or mode, the intended way to provision them is directly via systemd-tmpfiles.

Example

Consider a preserved file /foo/bar/baz:

preservation.preserveAt."/persistent".files = [
  { file = "/foo/bar/baz"; user = "baz"; group = "baz"; };
];

This would create the file with desired ownership on both the volatile and persistent volumes. However, the parent directories that did not exist before, i.e. /foo and /foo/bar, are created with ownership root:root and mode 0755.

Preservations allows the configuration of immediate parents, so the permissions for /foo/bar can be configured:

preservation.preserveAt."/persistent".files = [
  {
    file = "/foo/bar/baz"; user = "baz"; group = "baz";
    configureParent = true;
    parent.user = "baz";
    parent.group = "bar";
  };
];

Now the parent directory /foo/bar is configured with ownership baz:bar. But the first path component /foo still has systemd-tmpfiles' default ownership and the configuration becomes quite convoluted.

Solution

To create or configure intermediate path components of a persisted path, systemd-tmpfiles may be used directly:

# configure preservation of single file
preservation.preserveAt."/persistent".files = [
  { file = "/foo/bar/baz"; user = "baz"; group = "bar"; };
];

# create and configure parents of preserved file on the volatile volume with custom permissions
# The Preservation module also uses `settings.preservation` here.
systemd.tmpfiles.settings.preservation = {
  "/foo".d = { user = "foo"; group = "bar"; mode = "0775"; };
  "/foo/bar".d = { user = "bar"; group = "bar"; mode = "0755"; };
};

See tmpfiles.d(5) for available configuration options.

Library and Testing

Library

The functionality that is used in the module to discover the files and directories that are persisted and to generate the corresponding tmpfiles config and mount units is available from lib.nix. It is also available from the flake lib output.

In both cases it needs to be instantiated with the nixpkgs lib.

Testing

The integration test(s) can be found in /tests.