Getting Started
Use the following command to get a working flake: one NixOS host with a user, plus a standalone HomeManager config.
nix flake init -t github:vic/nest#defaultInit Flake Structure
Section titled “Init Flake Structure”Like Den before it, Nest is also flake agnostic, it can be used with or without flakes and with vanilla
lib.evalModulesorflake-partsor any other module system you choose. For this guide we use vanilla Flakes.
The following snippet creates our example flake that will load vanilla Nix modules from ./modules.
{ # Nest has no dependencies other than Nix builtins. inputs.nest.url = "github:denful/nest";
# Following dependencies are for showing how to integrate # Pick Darwin instead of NixOS or Hjem instead of HM and adapt. inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; inputs.home-manager.url = "github:nix-community/home-manager"; inputs.import-tree.url = "github:denful/import-tree";
outputs = inputs: inputs.nixpkgs.lib.evalModules { modules = [ (inputs.import-tree ./modules) ]; specialArgs = { inherit inputs; }; };}Have a module that loads nest.* options and setups nest arg.
{ inputs, ... }: { imports = [ inputs.nest.module ]; }Traits classify configurable types
Section titled “Traits classify configurable types”Traits tell Nest what each node is and how to build its config. You define them once:
Feel free to organize files as you see fit.
-
host→ NixOSnixpkgs.lib.nixosSystemfromnixosclass modules.modules/traits/host.nix {nest.trait.host = {# A host output is NixOS instance from all nixos modules for itclass.nixos = select: modules: inputs.nixpkgs.lib.nixosSystem {inherit modules; inherit (select.node) system;};};} -
user→ contributeshomeManagermodules to the parent hostnixosclass.modules/traits/user.nix {nest.trait.user = {class.homeManager = { host, user, ... }: modules:let# forward the hm modules to the right place at host confighome-manager.users."${user.name}".imports = modules;class = if lib.hasSuffix "darwin" host.systemthen "darwin" else "nixos";in {${class} = { inherit home-manager; }; # propagated up on tree};};} -
home→ standalone HMhome-manager.lib.homeManagerConfigurationmodules/traits/home.nix {nest.trait.home = {class.homeManager = select: modules: inputs.home-manager.lib.homeConfiguration {pkgs = inputs.nixpkgs.legacyPackages.${select.node.system};inherit modules;};};}
The DOM
Section titled “The DOM”Your infrastructure lives under nest.*. Everything outside nest.{trait,rules} is part of the DOM.
Attrsets with is = [traits...] are marked as nodes. Everything else is a grouping namespace sub-tree that passes attributes down.
graph TD
R["nest (root)"]
H["igloo\ntrait: host\nsystem: x86_64-linux"]
U["tux\ntrait: user\nadmin: true"]
HM["tux (standalone)\ntrait: home"]
R --> H
H --> U
R --> HM
{ nest, ... }:{ nest.igloo = { is = [ nest.host ]; system = "x86_64-linux";
# Remove for real hardware. boot = false;
# User HM tux = { is = [ nest.user ]; admin = true; }; };
# standalone HM nest.tux = { is = [ nest.home ]; system = "x86_64-linux"; };}Rules contribute Nix config
Section titled “Rules contribute Nix config”Rules match nodes by selector and contribute config to them.
# Defaults for all hostsnest.rules.host = { nixos.system.stateVersion = "25.11";};
# Applies only when the host has at least one HomeManager usernest.rules."host:has(.homeManager)" = { nixos.imports = [ inputs.home-manager.nixosModules.home-manager ];};
# Applies to every usernest.rules."host .user" = { user.isNormalUser = true;};
# Applies only to admin usersnest.rules."host .user[admin=true]" = { user.extraGroups = [ "wheel" ];};Rule and Selector Syntax
Section titled “Rule and Selector Syntax”Selector syntax support:
- String based CSS-like syntax:
"host > user" - Nix DSL:
[ (nest.within nest.host) nest.user ]
Rule syntax support:
-
CSS-like lists
nest.rules = [ { is = "host > user"; nixos = ...; } ]; -
Nix Attrsets
nest.rules."host > user".nixos = ...;
Configuration Arguments
Section titled “Configuration Arguments”Each nix class on a rule, like nixos, takes select as first argument.
select is used to query the current node or children or other nodes on the DOM.
nest.rules."host" = { nixos = select: { networking.hostName = select.node.name; };};It is also possible to destructure the first argument like:
nest.rules."host > user[admin=true]" = { homeManager = { user, host, select, pkgs, lib, ... }: { programs.git.settings.user.email = "${user.name}@${user.host}"; };};On this function:
user,hostare the closest parent node (or self) with that trait.pkgs,libare not parent nodes and are deferred for module eval.selectis the DOM query utility.
Outputs
Section titled “Outputs”outs.nix routes results to flake outputs:
{ config, ... }:let result = config.flake.nest.evalResult; in{ flake.nixosConfigurations = result.byClass.nixos or {}; flake.homeConfigurations = result.byClass.homeManager or {};}byClass groups every generated config by class.
Add a new host to your DOM, it appears in
nixosConfigurations automatically.