| name | nix |
| description | Nix Best Practices |
Nix Best Practices
Comprehensive guide for working with Nix, including flakes, NixOS, home-manager, nix-darwin, and development environments.
Core Principles
1. Use Flakes for Reproducibility
Always prefer flakes over channels for new projects. Flakes provide:
- Pinned dependencies via
flake.lock
- Hermetic evaluation (no NIX_PATH dependencies)
- Standardized project structure
- Composable inputs and outputs
2. Enable Flakes
# In configuration.nix or nix.conf
nix.settings.experimental-features = [ "nix-command" "flakes" ];
3. Directory Structure (Snowfall-style Pattern)
nixos-config/
├── flake.nix # Entry point
├── flake.lock # Pinned dependencies
├── systems/ # Per-machine configs
│ ├── x86_64-linux/
│ │ └── hostname/default.nix
│ └── aarch64-darwin/
│ └── hostname/default.nix
├── modules/
│ ├── nixos/ # NixOS-specific modules
│ ├── darwin/ # macOS-specific modules
│ └── home/ # Home-manager (cross-platform)
├── homes/ # Per-user home-manager configs
├── packages/ # Custom packages
└── secrets/ # Encrypted secrets (sops-nix)
4. Configuration Layering
Configurations build up in layers, each can override the previous:
- Linux: flake.nix → systems → modules/nixos → modules/home
- macOS: flake.nix → systems → modules/darwin → modules/home
5. Avoid Common Anti-patterns
# BAD: Implicit scope with `with`
environment.systemPackages = with pkgs; [ git vim wget ];
# GOOD: Explicit references
environment.systemPackages = [ pkgs.git pkgs.vim pkgs.wget ];
# Or use a let binding
environment.systemPackages = let p = pkgs; in [ p.git p.vim p.wget ];