nix-guidelines
Use when writing Nix code for flakes, modules, or packages
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Nix Guidelines
Guidelines for idiomatic Nix code with flakes and modules.
When to Use
- Writing flake.nix files
- Creating NixOS/Home Manager modules
- Writing Nix packages
- Reviewing Nix code
- Fixing "attribute not found" errors
- Debugging infinite recursion issues
When NOT to Use
- Bash scripting (use writeShellApplication)
- Runtime configuration (use normal config files)
- Non-reproducible workflows
Critical Rule: No with Statements
NEVER use with statements. They:
- Break static analysis tools (nixd, nil)
- Create scope ambiguity
- Cripple IDE features
# ❌ WRONG
environment.systemPackages = with pkgs; [ git vim ];
meta = with lib; { license = licenses.mit; };
# ✅ CORRECT
environment.systemPackages = [ pkgs.git pkgs.vim ];
meta = { license = lib.licenses.mit; };
Core Principles
- Explicit over Implicit - Always use explicit attribute paths
- Declarative Design - Think in systems, not scripts
- Pure Functions - Leverage Nix's functional nature
- nixfmt-rfc-style - Standard formatter
Flake Structure
{
description = "My project";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs"; # Deduplicate
};
};
outputs = { self, nixpkgs, home-manager, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
];
};
};
}
Module Structure
{
lib,
config,
pkgs,
...
}:
let
cfg = config.myNamespace.myModule;
in
{
options.myNamespace.myModule = {
enable = lib.mkEnableOption "my module";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.myPackage;
description = "The package to use";
};
settings = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Configuration settings";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
assertions = [
{
assertion = cfg.settings != { };
message = "myModule.settings cannot be empty when enabled";
}
];
};
}
Best Practices Checklist
- No
withstatements - Explicit destructuring in function signatures
- nixfmt-rfc-style formatting
- Options properly namespaced
- Input follows optimized (for flakes)
- Prefer let-in over rec
- lib.mkIf for conditional config
- Assertions for validation
- Comments for complex logic
Common Patterns
Conditional Configuration
config = lib.mkMerge [
(lib.mkIf cfg.enable {
services.myService.enable = true;
})
(lib.mkIf (cfg.enable && cfg.monitoring.enable) {
services.prometheus.exporters.myService.enable = true;
})
{
# Always applied with lower priority
services.myService.port = lib.mkDefault 8080;
}
];
Package Definition
{ lib, stdenv, fetchurl, openssl }:
stdenv.mkDerivation {
pname = "mypackage";
version = "1.0";
src = fetchurl {
url = "https://example.com/source.tar.gz";
sha256 = lib.fakeSha256;
};
buildInputs = [ openssl ];
meta = {
description = "My package";
license = lib.licenses.mit;
};
}
Script Wrapper
pkgs.writeShellApplication {
name = "my-script";
runtimeInputs = [ pkgs.jq pkgs.curl ];
text = ''
curl -s https://api.example.com | jq .
'';
}
Anti-Patterns to Avoid
Don't Use rec
# ❌ Avoid rec
rec {
a = 1;
b = a + 1;
}
# ✅ Use let-in
let
a = 1;
in {
inherit a;
b = a + 1;
}
Explicit Destructuring
# ❌ Wrong
args: with args; stdenv.mkDerivation { ... }
# ✅ Correct
{ stdenv, fetchurl, lib }: stdenv.mkDerivation { ... }
Performance Tips
- Use
inputs.nixpkgs.followsto deduplicate inputs - Split outputs for granular dependencies
- Profile with
NIX_SHOW_STATS=1 nix build - Consider
builtins.readFilefor large strings
Validation Commands
# Check flake
nix flake check
# Format code
nixfmt-rfc-style .
# Evaluate without building
nix eval .#packages.x86_64-linux.default
# Show flake outputs
nix flake show
Quick Reference
| Type | Syntax | Purpose |
|---|---|---|
| Bool | lib.types.bool |
Boolean value |
| String | lib.types.str |
Text value |
| Package | lib.types.package |
Nix package |
| List | lib.types.listOf type |
Homogeneous list |
| AttrSet | lib.types.attrsOf type |
Key-value pairs |
| Optional | lib.types.nullOr type |
Nullable type |
Common Errors
| Error | Cause | Fix |
|---|---|---|
| "infinite recursion" | Circular dependency in rec |
Use let-in instead |
| "attribute not found" | Typo or missing import | Check spelling, add to inputs |
| "cannot coerce to string" | Wrong type | Explicitly convert with toString |
| "with is a bad pattern" | Using with statement |
Use explicit paths |
Remember
Minor verbosity from explicit patterns is a feature - it makes code self-documenting and machine-readable.
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-26
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!