| name | Nix Ecosystem |
| description | This skill should be used when the user asks to "write nix", "nix expression", "flake.nix", "home-manager config", "programs.*", "services.*", or works with Nix language, flakes, or Home Manager. Provides comprehensive Nix ecosystem patterns and best practices. |
Provide comprehensive patterns for Nix language, flakes, and Home Manager configuration.
<nix_language>
Nix is lazily evaluated. Expressions are only computed when needed.
Only evaluates needed attributes
let
expensive = builtins.trace "Computing expensive" (1 + 1);
in
{ a = 1; b = expensive; }.a # Does not compute expensive
All Nix functions are pure. Same inputs always produce same outputs.
Pure function - always returns same result for same input
double = x: x * 2;
Avoid side effects; use derivations for build actions
buildResult = pkgs.stdenv.mkDerivation { ... };
Primary data structure in Nix
Basic attribute set
{ attr1 = value1; attr2 = value2; }
Access patterns
set.attr
set."attr-with-dashes"
Recursive attribute set
rec { a = 1; b = a + 1; }
Local bindings for complex expressions
let
helper = x: x + 1;
value = helper 5;
in
value * 2
Bring attribute set into scope
with pkgs; [ git vim tmux ]
Avoid nested with; prefer explicit references for clarity
Copy attributes from another set
{ inherit (pkgs) git vim; inherit name version; }
Modify or extend nixpkgs
final: prev: {
myPackage = prev.myPackage.override { ... };
}
Do you need to modify existing packages or add new ones globally?
Use overlays to extend nixpkgs
Use local package definitions with callPackage
Dependency injection pattern
myPackage = pkgs.callPackage ./package.nix { };
Standard package builder
pkgs.stdenv.mkDerivation {
pname = "mypackage";
version = "1.0.0";
src = fetchFromGitHub { ... };
nativeBuildInputs = [ pkgs.cmake ];
buildInputs = [ pkgs.openssl ];
installPhase = ''
mkdir -p $out/bin
cp mypackage $out/bin/
'';
}
Required attributes: pname, version, src
Standard phases: unpackPhase, patchPhase, configurePhase, buildPhase, installPhase
Dependency specification in derivations
{
# Tools run at build time (compilers, build tools)
nativeBuildInputs = [ cmake pkg-config ];
Libraries linked at runtime
buildInputs = [ openssl zlib ];
}
NixOS/Home Manager module structure
{ config, lib, pkgs, ... }:
{
options.myModule = {
enable = lib.mkEnableOption "my module";
setting = lib.mkOption {
type = lib.types.str;
default = "value";
description = "A setting";
};
};
config = lib.mkIf config.myModule.enable { # configuration when enabled
};
}
Define module options with types and defaults
options.myOption = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable my feature";
example = true;
};
Common types: lib.types.bool, lib.types.str, lib.types.listOf, lib.types.attrsOf
Shorthand for boolean enable option
enable = lib.mkEnableOption "my service";
<anti_patterns>
Directly referencing absolute paths breaks reproducibility
Use fetchurl, fetchFromGitHub, or relative paths within the repository
Multiple nested with statements reduce code clarity
Prefer explicit attribute access (pkgs.git) for better readability
Recursive attribute sets can be hard to understand and maintain
Use let-in for complex recursive definitions
Using string interpolation for path operations is error-prone
Use lib functions for path manipulation (lib.concatStringsSep, builtins.path)
Basic structure of a flake.nix file
{
description = "Project description";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, ... }@inputs: { # output attributes
};
}
Derivations for nix build
packages.x86_64-linux.default = pkgs.hello;
Development environments for nix develop
devShells.x86_64-linux.default = pkgs.mkShell {
packages = [ pkgs.nodejs ];
};
Runnable applications for nix run
apps.x86_64-linux.default = {
type = "app";
program = "${pkgs.hello}/bin/hello";
};
Nixpkgs overlays
overlays.default = final: prev: {
myPackage = prev.callPackage ./myPackage.nix { };
};
NixOS modules
nixosModules.default = { config, lib, pkgs, ... }: {
options.services.myService = { ... };
config = { ... };
};
Home Manager modules
homeManagerModules.default = { config, lib, pkgs, ... }: {
options.programs.myProgram = { ... };
config = { ... };
};
Full NixOS system configurations
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./configuration.nix ];
};
Home Manager configurations
homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
};
Reference GitHub repositories as flake inputs
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# Specific branch
stable.url = "github:NixOS/nixpkgs/nixos-24.11";
# Specific revision
pinned.url = "github:owner/repo/abc123def";
};
Share input between flakes to avoid duplication
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
Non-flake inputs for legacy repositories
my-source = {
url = "github:owner/repo";
flake = false;
};
Generate outputs for multiple systems
outputs = { self, nixpkgs, ... }:
let
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs systems;
in {
packages = forAllSystems (system:
let pkgs = nixpkgs.legacyPackages.${system};
in { default = pkgs.hello; }
);
};
Does your flake need to support multiple platforms?
Use per-system pattern with genAttrs or flake-utils
Define outputs for single system directly
Development environment with packages and hooks
devShells.default = pkgs.mkShell {
packages = with pkgs; [ nodejs yarn ];
shellHook = ''
echo "Development environment ready"
'';
};
Update all inputs to their latest versions
Regular dependency updates
Update a specific input
Selective updates to test compatibility
Display all flake outputs
Exploring available packages and configurations
Validate flake and run checks
CI/CD validation, pre-commit checks
<home_manager>
Standard Home Manager module structure
{ config, pkgs, lib, ... }:
{
options.custom.feature = {
enable = lib.mkEnableOption "feature description";
};
config = lib.mkIf config.custom.feature.enable { # configuration when enabled
};
}
Organize modules by program name
home-manager/programs/git.nix
home-manager/programs/neovim.nix
home-manager/programs/tmux.nix
Organize modules by category
home-manager/development/default.nix
home-manager/shell/default.nix
home-manager/editors/default.nix
Import multiple modules
imports = [
./programs/git.nix
./programs/neovim.nix
./shell/fish.nix
];
Enable a program with defaults
programs.git.enable = true;
Enable and configure a program
programs.git = {
enable = true;
userName = "name";
userEmail = "email";
extraConfig = {
core.editor = "nvim";
init.defaultBranch = "main";
};
};
Does Home Manager provide built-in module for this program?
Use programs.* with configuration options
Use home.file or xdg.configFile for manual configuration
Use alternative package version
programs.git = {
enable = true;
package = pkgs.gitFull;
};
Manage dotfiles directly
home.file.".config/app/config" = {
source = ./config;
# or
text = ''
key = value
'';
};
XDG config directory files
xdg.configFile."app/config".source = ./config;
Environment variables for login shells
home.sessionVariables = {
EDITOR = "nvim";
PAGER = "less";
};
Add directories to PATH
home.sessionPath = [ "$HOME/.local/bin" ];
<common_modules>
Git version control configuration
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "email@example.com";
signing = {
key = "KEY_ID";
signByDefault = true;
};
aliases = {
co = "checkout";
st = "status";
};
extraConfig = {
core.editor = "nvim";
};
};
Neovim editor configuration
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
plugins = with pkgs.vimPlugins; [
vim-commentary
vim-surround
];
extraConfig = ''
set number
set relativenumber
'';
extraLuaConfig = ''
vim.opt.expandtab = true
'';
};
Fish shell configuration
programs.fish = {
enable = true;
shellInit = ''
set -g fish_greeting
'';
shellAliases = {
ll = "ls -lah";
};
functions = {
gitignore = "curl -sL https://www.gitignore.io/api/$argv";
};
plugins = [
{ name = "z"; src = pkgs.fishPlugins.z.src; }
];
};
Terminal multiplexer configuration
programs.tmux = {
enable = true;
terminal = "screen-256color";
keyMode = "vi";
plugins = with pkgs.tmuxPlugins; [
sensible
yank
];
extraConfig = ''
set -g mouse on
'';
};
Directory-specific environment loader
programs.direnv = {
enable = true;
nix-direnv.enable = true;
enableBashIntegration = true;
enableZshIntegration = true;
};
<best_practices>
Use programs.* when available instead of manual configuration
Set home.stateVersion to your initial HM version and do not change after initial setup unless migrating
Group related configurations in separate modules for maintainability
Use lib.mkIf for conditional configuration
Prefer xdg.configFile over home.file for XDG-compliant apps
Use home.packages for additional packages not configured via programs.*
Track Home Manager state version for compatibility
home.stateVersion = "24.11"; # Current stable. 25.05 (upcoming).
Do not change after initial setup unless migrating
HM 24.11+ (25.05 upcoming) supports minimal mode for faster evaluation
imports = [
"${modulesPath}/programs/fzf.nix"
];
Advanced users optimizing evaluation time
Understand Nix expression requirements
1. Identify target: flake, module, derivation, or expression
2. Check existing patterns in project
3. Consult Serena memories for conventions
Write idiomatic Nix code
1. Follow patterns from decision trees
2. Use appropriate lib functions
3. Apply best practices for target type
Verify Nix expression correctness
1. Check syntax with nix flake check
2. Verify evaluation with nix eval
3. Test build with nix build
<error_escalation>
Style inconsistency in Nix expression
Note issue, suggest formatting
Evaluation error or type mismatch
Debug with --show-trace, fix expression
Build failure in derivation
Analyze build log, present options to user
Impure expression breaking reproducibility
Block operation, require pure alternatives
</error_escalation>
Use lib functions for complex operations
Follow project's existing Nix patterns
Maintain reproducibility in all expressions
Impure paths and absolute references
Nested with statements
Overusing rec for attribute sets
<related_agents>
Architecture and module dependency analysis for Nix configurations
Implementation of flake outputs, Home Manager modules, and NixOS configurations
Nix expression validation, formatting, and best practices enforcement
</related_agents>
<related_skills>
Symbol operations for navigating Nix expressions and module definitions
Fetch latest nixpkgs and Home Manager documentation
Debug evaluation errors and understand derivation failures
</related_skills>
Basic NixOS configuration with Home Manager
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
];
specialArgs = { inherit inputs; };
};
Standalone Home Manager without NixOS
homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
extraSpecialArgs = { inherit inputs; };
};
Home Manager as a NixOS module
{
imports = [ home-manager.nixosModules.home-manager ];
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
}