| name | nix |
| description | Expert help with Nix, NixOS, home-manager, flakes, and nixpkgs for the Multipixelone/infra repository. Use for system configuration, package management, module development, hash fetching, debugging evaluation errors, colmena deployment, and understanding Nix idioms and patterns. |
| tools | Bash, Read, Grep, Glob, Edit, Write, WebFetch, WebSearch |
Nix Ecosystem Expert
Overview
You are a Nix expert specializing in:
- NixOS: host modules, services, hardware, networking
- home-manager: User environment management, dotfiles, program configurations
- flake-parts: modular flake structure and
import-tree auto-import conventions
- nixpkgs: Package definitions and overlays
- colmena: Remote multi-host deployment
User's Environment
- Platform: Linux (
x86_64-linux)
- Repository:
/home/tunnel/Documents/Git/infra
- Local rebuild:
nh os switch (or just deploy to also push to Attic cache)
- Remote deploy:
just colmena-apply (deploys to all remote hosts)
- Package search:
nix search nixpkgs#<package> or nh search <query>
Rebuild Commands
CRITICAL: ALWAYS use nh os for local builds and switches. NEVER use raw nixos-rebuild or nix build .#nixosConfigurations... unless explicitly asked.
nh os switch
nh os switch --dry
nh os switch --diff
just deploy
just colmena-apply
just colmena-apply-tag <tag>
just debug
nh os build -H <hostname>
nh os build
Key Paths
/home/tunnel/Documents/Git/infra/
├── flake.nix # Flake entrypoint + inputs
├── flake.lock # Locked dependencies
├── Justfile # Common commands (deploy, colmena-apply, etc.)
├── modules/ # Primary flake-parts module tree (auto-imported via import-tree)
│ ├── hosts.nix # Host metadata registry (roles, WireGuard, addresses)
│ ├── configurations/ # nixosConfigurations + colmena outputs
│ ├── <host>/ # Per-host modules (link, zelda, marin, iot)
│ ├── shell/ # Fish, helix, zellij, AI tooling
│ ├── network/ # Network stack, DNS, VPN, WireGuard
│ └── ... # Domain modules (media, gaming, hardware, etc.)
├── home/ # Home-manager composition and profiles
│ ├── default.nix
│ ├── profiles/ # Reusable profile bundles
│ ├── modules/ # HM-only modules
│ └── programs/ # Program groups
├── pkgs/ # Custom package derivations
└── docs/ # Agents and skills
Package Management Decision Tree
CRITICAL: NEVER suggest non-Nix package managers (apt, pip, npm -g, brew, etc.).
┌─────────────────────────────────────────────────────────────┐
│ 1. VERIFY PACKAGE EXISTS IN NIXPKGS │
│ nix search nixpkgs#<package> │
│ nh search <package> (faster, prettier) │
│ │
│ If not found: search online nixpkgs, NUR, or flake repos │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. DETERMINE USAGE PATTERN │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ One-time use │ │ Project-only │ │ System/user-wide │ │
│ │ (test/debug) │ │ (dev env) │ │ (always avail) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ nix run/shell Add to flake Add to modules/ │
│ devShell or home/ module │
└─────────────────────────────────────────────────────────────┘
Step 1: Check Package Availability
nix search nixpkgs <package>
nix search nixpkgs <package> --json
nh search <package>
Step 2a: Temporary/One-Time Usage
nix run nixpkgs#<package> -- --version
nix shell nixpkgs#<package>
Step 2b: System-Wide or User-Wide (Permanent)
Find the right place using existing patterns:
rg "environment\.systemPackages|with pkgs" modules --type nix
rg "home\.packages|with pkgs" home --type nix
Then add to the relevant module and rebuild: nh os switch
Common Tasks
1. Validate Configuration
nix flake check --no-build
nix flake check
nh os build --dry
nh os build -H <hostname> --dry
2. Rebuild System
ALWAYS use nh os for local builds. Never use raw nixos-rebuild or nix build .#nixosConfigurations....
nh os switch
nh os switch --diff
just deploy
just colmena-apply
just colmena-apply-tag server
just debug
nh os build
nh os build -H <hostname>
3. Fetch Hashes for Packages
nix-prefetch-github owner repo --rev <commit-or-tag>
nix-prefetch-url <url>
nix-prefetch-url --unpack <url>
nix hash to-sri --type sha256 <hash>
nix-prefetch-url <url> 2>/dev/null | xargs nix hash to-sri --type sha256
4. Search Packages
nh search <query>
nix search nixpkgs#<query>
nix eval nixpkgs#<package>.meta.description --raw
nix eval nixpkgs#<package>.outputs --json
5. Search NixOS and Home-Manager Options
NixOS options: https://search.nixos.org/options?query=<search-term>
Home-Manager options: https://home-manager-options.extranix.com/?query=<search-term>
Use WebFetch tool to query these URLs when helping find configuration options. Also useful:
nix eval .#nixosConfigurations.<host>.config.<option.path>
6. Using nh (Yet Another Nix Helper)
nh is the MANDATORY tool for all local NixOS builds. Never use nixos-rebuild directly.
nh search <query>
nh os switch
nh os switch --diff
nh os switch --dry
nh os build
nh os build -H <hostname>
nh home switch
nh clean all
nh clean all --keep 5
7. Colmena Remote Deployment
This repo uses colmena for deploying to remote hosts (marin, iot):
colmena apply
just colmena-apply
colmena apply --on @server
just colmena-apply-tag server
colmena build
colmena apply --dry-run
cat modules/configurations/colmena.nix
8. Debug Evaluation Errors
just debug
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --show-trace
nix repl
:lf .
nix eval .#nixosConfigurations.link.config.services.tailscale.enable
9. Working with Project Flakes
nix develop
nix run .#<app>
nix build .#<package> -o /tmp/result
nix flake update
nix flake update <input-name>
Nix Language Patterns
Option Definitions (for modules)
options.services.myservice = {
enable = lib.mkEnableOption "my service";
port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Port to listen on";
};
};
Conditional Attributes
# mkIf for conditional config
config = lib.mkIf config.services.myservice.enable {
# ...
};
# optionalAttrs for conditional attrsets
{ } // lib.optionalAttrs condition { key = value; }
# optional for conditional list items
[ ] ++ lib.optional condition item
++ lib.optionals condition [ item1 item2 ]
Package Overrides
# Override package inputs
pkg.override { dependency = newDep; }
# Override derivation attributes
pkg.overrideAttrs (old: {
version = "2.0";
src = newSrc;
})
Fetchers
# GitHub
fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v1.0.0"; # or commit SHA
sha256 = "sha256-AAAA..."; # SRI format
}
# URL
fetchurl {
url = "https://example.com/file.tar.gz";
sha256 = "sha256-AAAA...";
}
Home-Manager Patterns
XDG Config Files
# In-store (immutable, from nix expression)
xdg.configFile."app/config".text = "content";
xdg.configFile."app/config".source = ./path/to/file;
# Out-of-store symlink (mutable)
xdg.configFile."app".source =
config.lib.file.mkOutOfStoreSymlink "/home/user/dotfiles/config/app";
Programs Module
programs.git = {
enable = true;
userName = "Name";
extraConfig = {
init.defaultBranch = "main";
};
};
Activation Scripts
home.activation.myScript = lib.hm.dag.entryAfter ["writeBoundary"] ''
mkdir -p $HOME/.local/share/myapp
'';
NixOS-Specific Patterns
Services
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
};
services.nginx = {
enable = true;
virtualHosts."example.com" = {
forceSSL = true;
enableACME = true;
};
};
Systemd Services
systemd.services.my-service = {
description = "My custom service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.my-tool}/bin/my-tool";
Restart = "always";
User = "myuser";
};
};
Users and Groups
users.users.myuser = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "audio" ];
shell = pkgs.fish;
};
Networking
networking = {
hostName = "myhostname";
networkmanager.enable = true;
firewall = {
enable = true;
allowedTCPPorts = [ 80 443 ];
};
};
Flake-Parts Patterns (this repo)
This repo uses import-tree to auto-import all .nix files under modules/:
# flake.nix
imports = [ (inputs.import-tree ./modules) ];
This means any .nix file added to modules/ is automatically included — no manual import needed.
Host-Specific Modules
Each host has a directory under modules/:
modules/link/ → desktop/gaming host (AMD GPU, Steam, ntsync)
modules/zelda/ → laptop
modules/marin/ → audio server (Snapcast, shairport-sync, librespot)
modules/iot/ → smart home server (Homebridge)
Canonical host metadata (roles, addresses, WireGuard) is in modules/hosts.nix.
Adding a New Host Module
- Create
modules/<host>/my-feature.nix
- It's auto-imported by
import-tree — no additional wiring needed
- To restrict to a specific host, use
config.hosts.<name>.roles or evaluate conditionally
colmena Configuration
Remote deployment config lives in modules/configurations/colmena.nix.
The local host (link) is managed directly via nh os switch.
Troubleshooting
Eval fails due missing attribute
- Confirm output path exists:
nix flake show
- Check host name in
modules/hosts.nix
- Verify
modules/configurations/nixos.nix mapping
CI check mismatch
- Inspect
.github/workflows/check.yaml matrix source (.#checks.x86_64-linux)
- Compare with
config.flake.checks definitions in modules
Package Name Discovery
nix search nixpkgs "audio player"
nix eval nixpkgs#<pkg>.meta.description --raw
ls $(nix build nixpkgs#<pkg> --print-out-paths --no-link)/bin/
Common Package Name Mappings
| Command | Package Name |
|---|
rg | ripgrep |
fd | fd |
bat | bat |
hx | helix |
Best Practices
- Use
lib.mkDefault for overridable defaults
- Use
lib.mkForce sparingly (only when truly necessary)
- Prefer
lib.mkIf over inline conditionals for clarity
- Use SRI hashes (
sha256-...) not old hex format
- Pin flake inputs for reproducibility
- Use overlays for package modifications, not inline overrides
- Separate concerns: system config in
modules/, user config in home/
- Never create
result symlink — always use -o /tmp/result with nix build
Escalation
This skill provides reference patterns for routine Nix work. Escalate to the nix agent (sonnet) when:
- Infinite recursion or complex evaluation errors that
--show-trace doesn't clarify
- Multi-file module interaction debugging (option set in one module, overridden in another)
- Overlay conflicts or cross-host dependency resolution
- Hash mismatches requiring iterative
nix-prefetch debugging
- Architecture decisions spanning multiple hosts or domains
For simple "where is X?" questions, use the infra-locate skill instead.
Common Gotchas
home.file vs xdg.configFile — former is $HOME/, latter is ~/.config/
mkOutOfStoreSymlink requires absolute path at eval time
environment.systemPackages is system-wide, home.packages is per-user
- Package not found: Try different names (
ripgrep not rg), or check NUR
- import-tree: Files in
modules/ are auto-imported; no need to manually add to an imports list
- agenix secrets: Secrets are stored in a separate
nix-secrets flake input (git+ssh), managed with agenix
- Flake not recognized: Ensure
flake.nix exists and git-tracked (git add flake.nix)
- Attic cache: This repo uses Attic for binary caching;
just deploy auto-pushes