nix-darwin
Installation
SKILL.md
nix-darwin Configuration
Architecture
Three-layer system: darwin modules (system) → home-manager (user) → homebrew (GUI apps).
flake.nix # Thin orchestration layer
├── hosts/workstation/ # Unstable packages, full toolchain
│ ├── default.nix → modules/darwin/
│ ├── home.nix → modules/home-manager/base.nix + program modules
│ └── homebrew.nix → modules/homebrew/base.nix + host casks
└── hosts/homelab/ # Stable packages (macOS 13), minimal
├── default.nix → modules/darwin/
├── darwin.nix # Host-specific overrides (mkForce)
├── home.nix → modules/home-manager/base.nix + neovim-stable
└── homebrew.nix
For full dependency flow and module layers, see references/architecture.md.
Core Conventions
Program Toggle Pattern
Every program module follows this structure (see templates/program-module.nix):
options.my.{program}.enable = mkEnableOption "description";
config = mkIf cfg.enable { /* config */ };
Enable in host's home.nix:
my.neovim.enable = true;
Adding a New Program
- Create
modules/home-manager/programs/{name}.nixusing template - Import it in the host's
home.nix - Set
my.{name}.enable = true - If it needs a GUI cask, add to
hosts/{host}/homebrew.nix
Configuration Management
| Method | When to Use | Example |
|---|---|---|
mkOutOfStoreSymlink |
Config that changes without rebuild | nvim, hyprspace |
builtins.readFile |
Config loaded into nix store | zed-editor |
| Inline nix | Simple, nix-native config | ghostty, starship |
Naming Conventions
- Nix files:
kebab-case.nix - Module options:
my.{program}.enable - Dotfile symlinks:
{name}.symlinkinsymlink/ - Hosts:
kebab-case - Commits: conventional (
feat(neovim):,fix(darwin):)
Essential Commands
See references/commands.md for full list.
# Apply configuration
sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake .#workstation
sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake .#homelab
# Validate without applying
nix flake check
# Update inputs
nix flake update
Gotchas
See references/gotchas.md for detailed explanations and fixes.
Build & System:
nix.daemonProcessType = "Background"prevents UI jank during builds- Determinate Nix conflicts with nix-darwin's nix management — disable one
followsnot set correctly causes closure size bloat
macOS Compatibility:
- homelab (macOS 13) needs
pkgs-stable— don't use unstable packages there - PAM services may need
lib.mkForce {}to override darwin defaults
Packages:
- Neovim tree-sitter CLI must be explicit in
extraPackages(not auto-included) - Homebrew cleanup can remove nix-managed packages — disable
autoCleanup - Homebrew cask vs nixpkgs: prefer nixpkgs for CLI tools, homebrew for GUI apps
Nix Patterns:
mkForcevsmkDefault— mkForce wins over everything, use sparinglymkOutOfStoreSymlinkpaths must be absolute strings, not nix paths
Community Patterns
When the user asks about improvements or alternative approaches, refer to references/community-patterns.md. Key patterns from high-star repos:
- mkDarwinConfig helper — reduces flake.nix boilerplate (malob, kclejeune)
- Multi-channel overlay —
pkgs.stable.Xanywhere vs specialArgs (malob) - Auto-import modules — directory scanning eliminates manual import lists (wimpysworld)
- Tag-based features — declare what a host IS, not what it HAS (wimpysworld)
- primaryUser alias — write
hm.programs.Xinstead of full path (kclejeune) - treefmt in flake — deadnix + nixfmt + statix (kclejeune)
- Automated flake lock updates — CI auto-PR + auto-merge (wimpysworld)