Skip to content

From Zero to Den

Our first step is creating a directory layout for your Den.

Terminal window
mkdir ./modules # Dendritic modules directory
cp -r /etc/nixos ./modules/_nixos # OPTIONAL existing NixOS modules
  1. All your Dendritic Nix modules will be loaded by import-tree automatically from ./modules

  2. (optional) Copy your existing NixOS modules into modules/_nixos.

    The _ is important for import-tree to ignore and NOT auto-load _nixos/configuration.nix nor _nixos/hardware-configuration.nix since they are regular NixOS modules and not Dendritic ones.

This step will use npins to fetch and lock some dependencies that will integrate all together:

Terminal window
npins init # adds nixpkgs channel
npins add github vic import-tree # for auto-importing ./modules
npins add github vic flake-aspects # aspects used by Den
npins add github vic den # Den itself, of course
npins add github vic with-inputs # flake-like inputs without Nix flakes.
npins add github nix-community home-manager # OPTIONAL home integration

Lets create default.nix. This file will serve as the entrypoint to all your configuration. Once you have default.nix you will very rarely need to touch it again.

default.nix
let
sources = import ./npins; # (1)
with-inputs = sources.with-inputs sources { }; # (2)
outputs = inputs:
(inputs.nixpkgs.lib.evalModules { # (3)
modules = [ (inputs.import-tree ./modules) ]; # (4)
specialArgs.inputs = inputs; # (5)
}).config.flake; # (6)
in
with-inputs outputs; # (7)

A tiny file, but a lof of things happening on it:

  1. Import fetched and already locked dependencies using npins.

  2. Flake like input follows and overrides via with-inputs.

    This ensures full compatibility between flakes and non-flakes worlds. You can share and re-use Dendritic modules from other people independently if they use flakes or not. Also in case you later move into flakes, nothing in your modules changes, just the entrypoint.

  3. Merges all imported modules into an actual unified config.

  4. Single usage of import-tree to load all modules recursively.

  5. Provide { inputs, ... } special argument to all modules.

  6. Read from Den’s flake output where nixosConfigurations are placed for compatibility between flakes and non-flakes environments.

  7. Invokes output with resolved inputs.

modules/den.nix
{ inputs, den, lib, ... }: {
imports = [ inputs.den.flakeModule ]; # (1)
den.schema.user.classes = lib.mkDefault [ "homeManager" ]; # (2)
den.hosts.x86_64-linux.igloo.users.tux = {}; # (3) (4)
den.aspects.igloo = { # (5)
includes = [ den.provides.hostname ]; # (6)
nixos = { pkgs, ... }: {
imports = [ ./_nixos/configuration.nix ]; # (7)
environment.systemPackages = [ pkgs.hello ];
};
};
den.aspects.tux = { # (8)
includes = [ den.provides.define-user den.provides.primary-user ]; # (9)
homeManager = { pkgs, ... }: {
packages = [ pkgs.vim ];
};
};
}

This is all that is happening at modules/den.nix.

  1. Imports den-framework features into the module system.

    This provides the den module argument as well as all den.* options, nixosConfigurations outputs.

  2. (optional, if you added home-manager dependency). This example shows how to enable homeManager integration for all users by default.

    Den supports multiple home environments, that is why none of them is enabled by default in Den. Freedom of choice is important, same as with flakes.

    Home Integration is a User concern, so it is configured at each independent <user>.classes or den.schema.user for all of them. See User Declaration and Homes Integration for more.

  3. Host definitions go inside den.hosts.

    This single line defines a host with hostName igloo, platform x86_64-linux from which class = "nixos" is derived, and a single user tux in it. See Declare Hosts & Users for more.

    Den automatically creates an aspect for each host that looks like this:

    den.aspects.igloo = parametric {
    nixos = {};
    };

    parametric means that this aspect is able to pass its context (the {host} definition for igloo into other aspects being included in it. See Parametric Aspects for more this.

  4. User definitions inside host.

    Hosts can have zero or more users. This example shows only one: tux.

    Each user can define its own configuration via freeform attributes, or define values for options from den.schema.user, like classes. See Schema Reference for more.

    den.hosts.x86_64-linux.igloo.users = {
    tux = {
    likes = [ "fishes"; ];
    classes = [ "homeManager" "hjem" ];
    };
    pingu = {
    likes = [ "music" ];
    classes = [ "maid" ];
    };
    };

    Just like hosts, users also get an aspect automatically created by Den from their definition:

    den.aspects.tux = parametric {
    homeManager = {};
    hjem = {};
    };
    den.aspects.pingu = parametric {
    maid = {};
    };
  5. Enhance the igloo host aspect.

    Remember that den.aspects.igloo was already created for you by Den from the host definition?. Well, in this step you are extending the aspect with more configuration. Any file can do this, contribute to any host or user aspect as they please. This is called Incremental Features in the Dendritic Advantages article.

  6. Include re-usable features from other, generic aspects.

    One of the advantages of flake-aspects’ upon which Den is built is automatic inclusion of other aspects. Unlike NixOS .imports, .includes are safe to use with conditionals because includes are not files to be imported.

    Den exploits flake-aspects by using contexts. A context like {host} is simply an attribute set that holds the igloo host definition from our examples. These are arguments to real Nix functions, NOT _module.args nor specialArgs. This is why these values can be used for conditional includes.

    Den provides some common batteries. Re-usable aspects like den.provides.hostname which is defined like this:

    den.provides.hostname = { host, ... }: { # host = igloo definition
    nixos.networking.hostName = host.hostName; # value from Host Schema
    };
  7. optional if you copied /etc/nixos into modules/_nixos) Import Non-Dendritic NixOS modules.

    The nixos attribute value is just a regular NixOS module. Because of this, it can import your previous ./_nixos/configuration.nix module or any other NixOS module from Nix libraries.

  8. Enhance the User aspect.

    As with the host aspect, Den also automatically created a user aspect. Here, you are extending it with more configuration.

  9. Include re-usable {host, user} batteries.

    Batteries like den.provides.primary-user take the {host, user} context where host is the igloo definition and user is the tux definition.

    These batteries also serve as example for you to create custom re-usable pieces of configuration shareable on several user/hosts or shared with the community.

Phew, lots of things we’ve learned! Our single and final step is

Terminal window
nixos-rebuild --file . -A nixosConfigurations.igloo

You can now head over to other guides or the reference:

Contribute Community Sponsor