Sharing a NixOS module codebase across NixOS versions

I have a set of NixOS module expressions, and use the “import everything, and use enable options” approach to managing how things are configured, and the NixOS modules are applied to machines that are running different versions of NixOS/nixpkgs.

Unfortunately, sometimes I have to use a new option that is only available in a later version of NixOS, and then when I try to build for the older version, I get an error like this:

The option `services.redis.settings' defined in `/home/ryantm/nixops/modules/mod1' does not exist.

This option exists in NixOS 21.05, but not NixOS 20.03. This happens even though the new option is configured behind a enable option that evaluates to false.

This error should not be triggered but that isn’t how the error is implemented currently.

The main workaround is to avoid importing the modules that configure new options on old systems, but this is not always possible or practical.

  1. Does anyone else have a similar setup to me?
  2. Have you discovered any other workarounds?
  3. Does someone familiar with the module system know if this error message can be moved to only trigger if the undefined option appears in the final configuration?
False, see below. Sorry!

This is what system.stateVersion is for.

Make sure the machines set the correct value for their version (you can do that with a little if-else block on their hostnames). For anything that isn’t captured correctly, you can then conditionally import your modules depending on which stateVersion is configured.

Are you sure the option doesn’t actually appear anywhere though? As far as I know, nix evaluates this lazily, so you shouldn’t see these symptoms.

I can have a *.stateVersion that is years back, but still beeing on unstable or 21.05.

The stateVersion isn’t expected to be changed once you move forward through the releases without taking a lot of care when bumping it.

Oh, you’re absolutely right. I suppose I’ve been confused about that option. Let me strikethrough that.

1 Does anyone else have a similar setup to me?
2 Have you discovered any other workarounds?

Sure, and the solution is pretty straight-forward (I don’t recall where I saw it so I cannot take credit for it).

Instead of dealing with the stateVersion which gets annoying, the trick is to look at options to see if the option is truly there.

Here is an example from my own zigbee2mqtt config:

services.zigbee2mqtt = {
  enable = !;
  package = (import <nixos-unstable> { }).zigbee2mqtt;
// lib.optionalAttrs (builtins.hasAttr "settings" {
  settings = {
    home_assistance = false;
    baudrate = 115200;

Thanks, that works great for me! I wasn’t aware of the options attrset being available like that.

Configuration is just another NixOS module so you can use anything you would in any other module.

1 Like