Forcing these inputs to follow the root nixpkgs changed the ComfyUI package's derivation hash, which invalidated the binary cache and queued a 91-derivation rebuild (the full ComfyUI Python closure plus cudnn, nccl and libnvshmem). Restore each input's own locked nixpkgs and the original flake.lock, which matches the already-built, cached closure. A dry-build now reports nothing to build. The single-nixpkgs cleanup is not worth forcing multi-hour CUDA rebuilds.
NixOS Configs
Reproducible multi-host NixOS + Hyprland ("Silicon Volt") configuration.
Install (new machine)
# Setup disks
nix run --experimental-features "nix-common flakes" github:nix-community/disko -- --mode disko --flake .#hades
# install configs
nixos-install --flake .#hades
# set password
nixos-enter --root /mnt -- passwd anish
Rebuild (existing machine)
rebuild switch # zsh helper: sudo nixos-rebuild switch --flake ~/NixOS-Configs#<host>
Adding a new host (e.g. a laptop)
- Create
hosts/<name>/withdefault.nix,hardware-configuration.nix, and (optionally)disko.nix. Shared modules (modules/core,modules/graphical,modules/apps) are already pulled in bycommonModulesinflake.nix— the host only adds its own hardware/disko config. - Register it in
flake.nixundernixosConfigurations. - Turn optional features on per machine via the shared
my.*options:
# hosts/<name>/default.nix
my = {
username = "anish"; # default; change for a different primary user
gaming.enable = false; # Steam, Gamemode, MangoHud
ai.local.enable = false; # Ollama, ComfyUI, Open WebUI
pi.enable = false; # pi coding agent (home-manager)
};
- Declare host-specific displays in
hosts/<name>/default.nix:
home-manager.users.anish.hypr.monitors = [
{
output = "eDP-1"; # or "desc:<panel description>"
mode = "2880x1800@120Hz"; # or "preferred"
position = "auto";
scale = 2;
vrr = null; # optional per-monitor VRR
}
{ output = ""; mode = "preferred"; position = "auto"; scale = "auto"; } # fallback
];
The hypr.monitors default (any output @ preferred) keeps every machine working
out of the box; hosts override only what they need. Hosts without an NVIDIA GPU
simply don't import modules/hardware/nvidia.nix — all vendor env vars live there,
while the vendor-neutral hardware.graphics stack lives in modules/graphical.
Feature toggles (my.*)
Optional app modules are always imported (via modules/apps/default.nix) but only
activate when a host sets the corresponding flag, so hosts never need to edit
import lists:
| Option | Enables |
|---|---|
my.username |
Primary user (users, home-manager, getty autologin) |
my.flakeDir |
Checkout path used by the rebuild helper and zshconf/hyprconf aliases |
my.gaming.enable |
Steam, Gamemode, MangoHud |
my.ai.local.enable |
Ollama (CUDA), ComfyUI, Open WebUI |
my.pi.enable |
pi coding agent (home-manager) |
Hardware modules remain explicit imports per host (e.g. modules/hardware/nvidia.nix).
Theming
The Silicon Volt palette lives in one place, home/theme.nix, and is passed to
every home-manager module through extraSpecialArgs. Tools that only accept a
raw config file (starship, rofi, the herdr picker) carry @color@ placeholders
that are substituted from the same palette at build time, so changing an accent
means editing one file.
Editing configs: live vs generated
Most configs are generated from Nix and symlinked read-only from the store, so
they need a rebuild switch to change:
foot.ini,hyprlock.conf,hypridle.conf,mako/config,starship.toml,rofi/config.rasi,.zshenv/.zshrc(Nix attrs, themed)
Three configs are instead symlinked straight from the repo with
mkOutOfStoreSymlink, so edits apply without a rebuild:
| Live file | Applies on |
|---|---|
home/hypr/hyprland.lua |
hyprctl reload |
home/hypr/quickshell/** |
quickshell restart |
home/nvim/config/** (incl. lazy-lock.json) |
next nvim start; :Lazy update writes here |
Because hyprland.lua is live, the palette and per-host monitors are emitted as
generated fragments, ~/.config/hypr/theme.lua and
~/.config/hypr/monitors.lua, which the live file loads with dofile. Edit the
Lua directly for anything else.
The tradeoff: live files are not part of the Nix closure, so the repo must exist
at my.flakeDir for the symlinks to resolve, and uncommitted edits can drift
from git. Keep them committed like any other tracked file.