Nixos-rebuild: undefined variable error on select instances of a variable (w/ flakes)

Hi! I’m working on setting up my first NixOS server as a side project and took some inspiration from Elliott over at Dreams of Autonomy.

Actually, I pretty much lifted his entire flakes config, and everything was working great on the first build (built on remote from my Apple Silicon Mac). I’ve started to do some more personalized config and suddenly, some of the original flake config seems to be throwing errors.

Here’s my error message

error:
       … while calling the 'seq' builtin

         at /nix/store/2vmkhm30kdnics1bnj9zgblh9yr4cpyw-source/lib/modules.nix:322:18:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          323|         _module = checked (config._module);

       … while evaluating a branch condition

         at /nix/store/2vmkhm30kdnics1bnj9zgblh9yr4cpyw-source/lib/modules.nix:261:9:

          260|       checkUnmatched =
          261|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |         ^
          262|           let

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: undefined variable 'meta'

       at /nix/store/nryspgg2lk3m956xkc7x1w64qpc0xmyb-source/configuration.nix:82:14:

           81| 	    "--disable local-storage"
           82|     ] ++ (if meta.hostname == "homelab-0" then [] else [
             |              ^
           83| 	      "--server https://homelab-0:6443"

The problem is, I haven’t changed anything in this block of configuration.nix (my k3s service) or the flake.nix that it should be sourced from either. I reverted all of my personalized changes, but a nixos-rebuild still fails with the error above. As far as I can tell from (re)reading the NixOS manual, everything looks correct. I even have a line further up in my configuration.nix that seems to resolve this variable value without any issues:

24|   networking.hostName = meta.hostname; # Define your hostname.

Here’s my flake.nix:

{
  description = "Homelab NixOS Flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    # Disko
    disko.url = "github:nix-community/disko";
    disko.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { self, nixpkgs, disko, ... }@inputs: let
    nodes = [
      "homelab-0"
      "homelab-1"
      "homelab-2"
    ];
  in {
    nixosConfigurations = builtins.listToAttrs (map (node: {
	  name = node;
	  value = nixpkgs.lib.nixosSystem {
        specialArgs = {
          meta = { hostname = node; };
        };
        system = "x86_64-linux";
        modules = [
          # Modules
	      disko.nixosModules.disko
	      ./hardware-configuration.nix
	      ./disko-config.nix
	      ./configuration.nix
	    ];
      };
    }) nodes);
  };
}

and here is the block of configuration.nix that throws the error in a rebuild:

     72|   services.k3s = {
     73|     enable = true;
     74|     role = "server";
     75|     tokenFile = /path/to/tokenFile;
     76|     extraFlags = toString ([
     77|             "--write-kubeconfig-mode \"0644\""
     78|             "--cluster-init"
     79|             "--disable servicelb"
     80|             "--disable traefik"
     81|             "--disable local-storage"
     82|     ] ++ (if meta.hostname == "homelab-0" then [] else [
     83|        "--server https://homelab-0:6443"
     84|     ]));
     85|     clusterInit = (meta.hostname == "homelab-0");
     86|   };

My only thought is that somehow the variable scoping is not as global as I thought, but the docs don’t seem to indicate anything like that. I suspect it’s probably something far simpler, but I am at a loss. I appreciate any help anyone is able to offer!

Attributes listed in specialArgs become module arguments.
But you need to list them explicitly at the top of your modules.

So, instead of (for example)

{ pkgs, ... }:
{
  # config here
}

you’d need

{ meta, pkgs, ... }:
{
  # config here
}

Also, I will also strongly that advise that conditionals based on hostname are an extremely bad idea, as it will cause you more confusion when debugging and writing code, and you should instead use separate entry-points for separate configurations.

You’re already using flakes, which makes this even easier, actually, just name the entry-point according to your host instead of shoving everything into one big configuration.nix, and add that entry-point file into modules. (Or create a folder named after your host, whichever you find easier to deal with.)

1 Like

The additional module argument was indeed missing. Cheers!

I agree, I’ve found the hostname-based config approach somewhat difficult. I’m still getting my footing with Nix, but what you’re suggesting sounds like a much more scalable approach. I will have to look into that for future enhancements :slight_smile: