16 KiB
Executable File
Flake Improvement Suggestions
A methodical review of the flake against what Snowfall Lib provides and what the codebase currently does. Suggestions are grouped by theme and ordered roughly from highest to lowest impact.
1. Flake-level: HM module registration — single source of truth via snowfall-lib fix
Root cause discovered: Snowfall Lib's mkFlake previously merged systems.modules.home into homes only for standalone homeConfigurations. The homes attrset passed to create-systems (which builds nixosConfigurations) was the raw unmerged value, so systems.modules.home had no effect on NixOS-integrated homes.
Fix applied: Patched the personal snowfall-lib fork (github:mjallen18/snowfall-lib) to extract the merge into a shared homes-with-system-modules binding and pass it to both create-homes (standalone) and create-systems (NixOS-integrated). flake.lock updated to the new commit.
modules/nixos/home/default.nix no longer needs sharedModules — systems.modules.home in flake.nix is now the single authoritative list for all contexts.
2. Flake-level: Duplicated Darwin HM module registration
Problem: Same issue as above for Darwin. flake.nix:160–167 registers Darwin HM modules via systems.modules.darwin, but none of those are actually Home Manager modules — nix-homebrew, home-manager.darwinModules.home-manager, nix-plist-manager, nix-rosetta-builder, nix-index-database, and stylix.darwinModules.stylix are all NixOS-style Darwin system modules, not HM sharedModules. This is the correct place for them. The modules/darwin/home/default.nix module handles the Darwin-side HM bridge.
No change needed here, but add a comment to clarify why this list stays in flake.nix while the modules.home list should move:
# Common darwin system-level modules (not HM sharedModules — those live in modules/darwin/home/)
modules.darwin = with inputs; [ ... ];
3. System-level: Repeated nebula lighthouse config
Problem: Three systems (matt-nixos, allyx, macbook-pro-nixos) each independently spell out the same lighthouse peer config:
# Repeated verbatim in 3 files:
lighthouses = [ "10.1.1.1" ];
staticHostMap = {
"10.1.1.1" = [ "mjallen.dev:4242" ];
};
port = 4242;
Suggestion: Add defaults to modules/nixos/services/nebula/default.nix options so that non-lighthouse nodes don't need to spell this out. Since this is a personal network with one lighthouse, the defaults can encode that:
# In nebula/default.nix options:
lighthouses = lib.mjallen.mkOpt (types.listOf types.str) [ "10.1.1.1" ]
"Nebula overlay IPs of lighthouse nodes";
staticHostMap = lib.mjallen.mkOpt (types.attrsOf (types.listOf types.str))
{ "10.1.1.1" = [ "mjallen.dev:4242" ]; }
"Static host map";
port = lib.mjallen.mkOpt types.port 4242 "Nebula listen port";
Client systems can then reduce to:
services.nebula = {
enable = true;
secretsPrefix = "matt-nixos/nebula";
secretsFile = lib.snowfall.fs.get-file "secrets/desktop-secrets.yaml";
hostSecretName = "matt-nixos";
};
The lighthouse (pi5) already overrides isLighthouse = true and doesn't set lighthouses/staticHostMap, so it would be unaffected.
4. System-level: systemd-networkd-wait-online scattered disablement
Problem: systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false appears in:
systems/x86_64-linux/matt-nixos/default.nix:92systems/x86_64-linux/allyx/default.nix:135
modules/nixos/network/default.nix already disables NetworkManager-wait-online and systemd.network.wait-online, but not systemd-networkd-wait-online. These are the same underlying concern.
Suggestion: Add systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false; unconditionally to modules/nixos/network/default.nix alongside the existing NetworkManager-wait-online disablement (line 89). Remove the per-system overrides.
5. System-level: coolercontrol and GNOME desktop environment variables
Problem: Two systems (matt-nixos:91, allyx:82) share identical config blocks:
programs.coolercontrol.enable = true;
environment.variables = {
GDK_SCALE = "1";
EDITOR = "${lib.getExe' pkgs.vscodium "codium"} --wait";
VISUAL = "${lib.getExe' pkgs.vscodium "codium"} --wait";
};
These belong to a desktop AMD gaming profile, not to the system configs themselves.
Suggestions (pick one or both):
- A — Add a
coolercontrol.enableoption tomodules/nixos/hardware/amd/default.nix(defaultfalse) and wireprograms.coolercontrol.enableinside it. Each system opts in withhardware.amd.coolercontrol.enable = true. - B — Add
vscodiumas the defaultEDITOR/VISUALtomodules/nixos/desktop/gnome/default.nixbehind avscodium.enableoption (defaultfalse). The two systems that want it setdesktop.gnome.vscodium.enable = true. - C — Create a shared
modules/nixos/desktop/common/default.nix(orprofiles/desktop.nix) that both GNOME and Hyprland modules consume, and putGDK_SCALEthere.
6. System-level: networking.networkmanager.wifi.backend = "iwd" bypass
Problem: matt-nixos:100 and allyx:140 set networking.networkmanager.wifi.backend = "iwd" directly, bypassing the ${namespace}.network.iwd.enable option that the network module already provides.
Looking at modules/nixos/network/default.nix:143–154, enabling cfg.iwd.enable does set this value via mkForce, but it also forces networkmanager.enable = mkForce false — which is unwanted on these systems that use NetworkManager with the iwd backend.
Root cause: The module conflates "use iwd" (the WiFi daemon) with "disable NetworkManager" (the connection manager). These are separate concerns. NetworkManager can use iwd as its WiFi backend while still being the connection manager.
Suggestion: Restructure the network module's iwd handling:
# Instead of forcing NM off when iwd is enabled:
networking = {
wireless.iwd.enable = cfg.iwd.enable;
networkmanager = mkIf cfg.networkmanager.enable {
enable = true;
wifi.backend = mkIf cfg.iwd.enable "iwd";
# ... rest of NM config
};
};
Then the per-system lines become:
${namespace}.network = {
hostName = "matt-nixos";
iwd.enable = true;
networkmanager.enable = true;
};
7. System-level: fileSystems."/etc".neededForBoot not in impermanence module
Problem: fileSystems."/etc".neededForBoot = true is set manually in four system configs (nuc-nixos, pi5, jallen-nas, graphical). This is a prerequisite of impermanence (tmpfs root), not a per-system choice.
Suggestion: Add to modules/nixos/impermanence/default.nix:
config = mkIf cfg.enable {
fileSystems."/etc".neededForBoot = true;
# ... existing config
};
Then remove the manual setting from each system. (macbook-pro-nixos and matt-nixos may already have this in their filesystems.nix — verify and remove duplicates there too.)
8. System-level: system.stateVersion and time.timeZone should be module options
Problem: In modules/nixos/system/default.nix:
- Line 3:
timezone = "America/Chicago"is hardcoded - Line 54:
system.stateVersion = "23.11"is hardcoded
Both are set unconditionally for every system with no way to override without using lib.mkForce.
Suggestions:
# modules/nixos/system/default.nix
{ config, lib, namespace, pkgs, system, ... }:
let
cfg = config.${namespace}.system;
in
{
options.${namespace}.system = {
timezone = lib.mkOption {
type = lib.types.str;
default = "America/Chicago";
description = "System timezone";
};
stateVersion = lib.mkOption {
type = lib.types.str;
default = "23.11";
description = "NixOS state version. Should match the version used when the system was first installed.";
};
};
config = {
time.timeZone = cfg.timezone;
system.stateVersion = cfg.stateVersion;
# ... packages
};
}
This maintains the current default for all systems (no change required) while allowing any system to say ${namespace}.system.stateVersion = "24.05" cleanly.
9. Module-level: Darwin and NixOS nix modules share ~90% of their content
Problem: modules/darwin/nix/default.nix and modules/nixos/nix/default.nix differ only in:
- Darwin lacks
daemonCPUSchedPolicy/daemonIOSchedClass/daemonIOSchedPriority - Darwin lacks the
systemd.services.nix-gc.serviceConfigblock - Darwin lacks
cudaSupport/rocmSupportinnixpkgs.config - Darwin's substituters list omits
attic.xuyh0120.win/lantian
Everything else — substituters, trusted keys, warn-dirty, experimental-features, trusted-users, builders-use-substitutes, connect-timeout, fallback, log-lines, max-free, min-free, GC settings, optimise — is identical.
Suggestion: Extract a shared Nix attrset into lib/nix-settings/default.nix (or a plain .nix file imported by both):
# lib/nix-settings/default.nix
{ lib }:
{
commonSubstituters = [
"http://jallen-nas.local:9012/nas-cache"
"https://nixos-apple-silicon.cachix.org"
"https://nixos-raspberrypi.cachix.org"
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
commonTrustedPublicKeys = [ ... ];
commonSettings = { warn-dirty = ...; experimental-features = ...; ... };
commonGc = { automatic = true; options = "--delete-older-than 30d"; };
}
Both modules import and spread this. The NixOS module adds scheduler policies and systemd GC service tweaks on top.
10. Module-level: Home SOPS configuration is inconsistent across homes
Problem: Three different patterns are used to configure SOPS in home configs:
${namespace}.sops.enable = true— uses the module atmodules/home/sops/default.nix(macbook-pro-nixos home, jallen-nas home)- Inline SOPS config — sets
sops.*directly (allyx home, pi5 home) - Nothing — some homes don't configure sops at all (matt-nixos home relies on system-level secrets only)
The modules/home/sops/default.nix module already handles the age.keyFile path, defaultSopsFile, and SSH key setup. The inline patterns duplicate this.
Suggestion: Migrate all homes that configure sops inline to use ${namespace}.sops.enable = true. If the home needs a different defaultSopsFile (e.g. pi5 uses secrets/pi5-secrets.yaml), that should be a module option:
# modules/home/sops/default.nix — add option:
options.${namespace}.sops = {
enable = lib.mkEnableOption "home sops";
defaultSopsFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null; # falls back to global secrets.yaml
description = "Override the default SOPS file for this home";
};
};
11. Module-level: modules/nixos/home/default.nix — home-manager input key coupling
Problem: systems.modules.nixos in flake.nix:147 explicitly includes home-manager.nixosModules.home-manager. However Snowfall Lib automatically injects the home-manager NixOS module when the home-manager input is present and there are home configurations (Snowfall Lib system/default.nix lines 265–270).
Suggestion: Verify (by temporarily removing the explicit entry) whether home-manager.nixosModules.home-manager can be dropped from systems.modules.nixos. If Snowfall Lib handles this automatically, removing it eliminates the manual coupling.
12. System-level: nuc-nixos — large monolithic default.nix
Problem: systems/x86_64-linux/nuc-nixos/default.nix is over 330 lines and contains everything inline: disk config, networking, Home Assistant dashboard definitions (~170 lines of inline Nix), kernel config, user setup, and services. Every other complex system (jallen-nas) already uses a split structure with apps.nix, services.nix, nas-defaults.nix, etc.
Suggestion: Extract into separate files following the jallen-nas pattern:
systems/x86_64-linux/nuc-nixos/
├── default.nix # thin: imports + top-level options
├── boot.nix # disk/luks/filesystem config
├── dashboard.nix # Home Assistant dashboard card definitions
├── services.nix # postgres, redis, HA, OTBR etc.
└── sops.nix # (or reuse the shared module)
The dashboard in particular (currently lines ~88–260) should be isolated so HA configuration changes don't require touching system-level config.
13. System-level: Verify admin@jallen-nas steam-rom-manager double-import
Problem: homes/x86_64-linux/admin@jallen-nas/default.nix:16 explicitly imports steam-rom-manager.homeManagerModules.default. This same module is injected globally via modules/nixos/home/default.nix:92 for all x86_64 systems (the ARM guard is !isArm, and jallen-nas is x86_64).
Suggestion: Remove the explicit import from admin@jallen-nas/default.nix. If it was added for standalone home-manager switch builds (without NixOS), document that reason in a comment rather than keeping a potentially conflicting double-import.
14. Flake-level: pi5 host entry with empty modules list
Problem: flake.nix:218–221 defines:
pi5 = {
modules = [ ];
};
An empty modules list is the default behavior — this entry has no effect and can be removed. The comment # disko is already in systems.modules.nixos above is incorrect (disko is global for all systems, not specific to pi5). The comment itself is misleading.
Suggestion: Remove the pi5 host entry from flake.nix entirely. If the comment is meant to remind future maintainers that disko is global, move that context to AGENTS.md or a comment near the global systems.modules.nixos list.
15. Flake-level: home-manager-stable input is pulled in but never used
Problem: flake.nix:10–13 defines home-manager-stable but home-manager = home-manager-unstable is the alias (line 21). No system or module references home-manager-stable directly. It adds to lock file churn and evaluation time.
Suggestion: Remove home-manager-stable unless there is a concrete plan to use it for a stable-channel system. If stable Home Manager support is desired in the future, add it back at that point.
16. Flake-level: Consider using Snowfall Lib alias for formatter output
Problem: The outputs-builder in flake.nix:277–280 is used only to register the treefmt formatter. Snowfall Lib supports an alias mechanism and also allows outputs-builder to be used, but this is the only use of outputs-builder in the entire flake.
Suggestion: This is fine as-is, but note that outputs-builder output can be overridden by auto-discovery. Since the formatter isn't auto-discovered, outputs-builder is the correct approach. No change needed — but the comment on line 279 about the mjallen-lib overlay being auto-discovered is accurate and good to keep.
Summary Table
| # | Location | Type | Effort | Impact |
|---|---|---|---|---|
| 1 | flake.nix |
Deduplication | Low | High — removes confusing double-registration |
| 2 | flake.nix |
Documentation | Low | Low |
| 3 | nebula/default.nix |
Better defaults | Low | Medium — 3 systems simplified |
| 4 | network/default.nix |
Consolidation | Low | Medium — remove per-system workarounds |
| 5 | hardware/amd + desktop/gnome |
New options | Medium | Medium — DRY gaming desktop profile |
| 6 | network/default.nix |
Bug fix / refactor | Medium | High — current iwd handling is incorrect |
| 7 | impermanence/default.nix |
Consolidation | Low | Medium — remove 4 manual entries |
| 8 | system/default.nix |
New options | Low | Medium — allows per-system overrides cleanly |
| 9 | lib/ + darwin/nix + nixos/nix |
Extraction | Medium | Medium — single source of truth for nix config |
| 10 | homes/*/ + modules/home/sops |
Consistency | Low | Low — consistency improvement |
| 11 | flake.nix |
Simplification | Low | Low — possible dead entry |
| 12 | systems/nuc-nixos/ |
Refactor | Medium | High — maintainability |
| 13 | homes/admin@jallen-nas |
Bug fix | Trivial | Low — potential double-import |
| 14 | flake.nix |
Cleanup | Trivial | Low — dead code |
| 15 | flake.nix |
Cleanup | Trivial | Low — reduces lock churn |
| 16 | flake.nix |
N/A | None | No change needed |