| 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. |
| version | 0.1.0 |
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 { ... };
}
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; }
);
};
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";
};
};
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
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;
}