--- name: nix-best-practices description: Nix patterns for flakes, overlays, unfree handling, and binary overlays. Use when working with flake.nix or shell.nix. --- # Nix Best Practices ## Flake Structure Standard flake.nix structure: ```nix { description = "Project description"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ # packages here ]; }; }); } ``` ## Follows Pattern (Avoid Duplicate Nixpkgs) When adding overlay inputs, use `follows` to share the parent nixpkgs and avoid downloading multiple versions: ```nix inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # Overlay follows parent nixpkgs some-overlay.url = "github:owner/some-overlay"; some-overlay.inputs.nixpkgs.follows = "nixpkgs"; # Chain follows through intermediate inputs another-overlay.url = "github:owner/another-overlay"; another-overlay.inputs.nixpkgs.follows = "some-overlay"; }; ``` All inputs must be listed in outputs function even if not directly used: ```nix outputs = { self, nixpkgs, some-overlay, another-overlay, ... }: ``` ## Applying Overlays Overlays modify or add packages to nixpkgs: ```nix let pkgs = import nixpkgs { inherit system; overlays = [ overlay1.overlays.default overlay2.overlays.default # Inline overlay (final: prev: { myPackage = prev.myPackage.override { ... }; }) ]; }; in ``` ## Handling Unfree Packages ### Option 1: nixpkgs-unfree (Recommended for Teams) Use numtide/nixpkgs-unfree for EULA-licensed packages without requiring user config: ```nix inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable"; nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs"; # Unfree overlay follows nixpkgs-unfree proprietary-tool.url = "github:owner/proprietary-tool-overlay"; proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree"; }; ``` This chains: `proprietary-tool` → `nixpkgs-unfree` → `nixpkgs` ### Option 2: User Config Users add to `~/.config/nixpkgs/config.nix`: ```nix { allowUnfree = true; } ``` ### Option 3: Specific Packages (Flake) ```nix let pkgs = import nixpkgs { inherit system; config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "specific-package" ]; }; in ``` Note: `config.allowUnfree` in flake.nix doesn't work with `nix develop` - use nixpkgs-unfree or user config. ## Creating Binary Overlay Repos When nixpkgs builds a community version lacking features (common with open-core tools), create an overlay that fetches official binaries. ### Pattern (see 0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay) ```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; version = "1.0.0"; # Platform-specific binaries sources = { "x86_64-linux" = { url = "https://example.com/tool-linux-amd64-v${version}"; sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; }; "aarch64-linux" = { url = "https://example.com/tool-linux-arm64-v${version}"; sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; }; "x86_64-darwin" = { url = "https://example.com/tool-darwin-amd64-v${version}"; sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC="; }; "aarch64-darwin" = { url = "https://example.com/tool-darwin-arm64-v${version}"; sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD="; }; }; source = sources.${system} or (throw "Unsupported system: ${system}"); toolPackage = pkgs.stdenv.mkDerivation { pname = "tool"; inherit version; src = pkgs.fetchurl { inherit (source) url sha256; }; sourceRoot = "."; dontUnpack = true; installPhase = '' mkdir -p $out/bin cp $src $out/bin/tool chmod +x $out/bin/tool ''; meta = with pkgs.lib; { description = "Tool description"; homepage = "https://example.com"; license = licenses.unfree; # or appropriate license platforms = builtins.attrNames sources; }; }; in { packages.default = toolPackage; packages.tool = toolPackage; overlays.default = final: prev: { tool = toolPackage; }; }) // { overlays.default = final: prev: { tool = self.packages.${prev.system}.tool; }; }; } ``` ### Getting SHA256 Hashes ```bash nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0 # Returns hash in base32, convert to SRI format: nix hash to-sri --type sha256 ``` Or use SRI directly: ```bash nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0 ``` ## Dev Shell Patterns ### Basic Shell ```nix devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ nodejs python3 ]; shellHook = '' echo "Dev environment ready" ''; }; ``` ### With Environment Variables ```nix devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ postgresql ]; # Set at shell entry DATABASE_URL = "postgres://localhost/dev"; # Or in shellHook for dynamic values shellHook = '' export PROJECT_ROOT="$(pwd)" ''; }; ``` ### Native Dependencies (C Libraries) ```nix devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ openssl postgresql ]; # Expose headers and libraries shellHook = '' export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH" export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH" export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH" ''; }; ``` ## Direnv Integration `.envrc` for flake projects: ```bash use flake ``` For unfree packages without nixpkgs-unfree: ```bash export NIXPKGS_ALLOW_UNFREE=1 use flake --impure ``` ## Common Commands ```bash # Update all inputs nix flake update # Update specific input nix flake update some-input # Check flake validity nix flake check # Show flake metadata nix flake metadata # Enter dev shell nix develop # Run command in dev shell nix develop -c # Build package nix build .#packageName # Run package nix run .#packageName ``` ## Troubleshooting ### "unexpected argument" Error All inputs must be listed in outputs function: ```nix # Wrong outputs = { self, nixpkgs }: ... # Right (if you have more inputs) outputs = { self, nixpkgs, other-input, ... }: ... ``` ### Unfree Package Errors with nix develop `config.allowUnfree` in flake.nix doesn't propagate to `nix develop`. Use: 1. nixpkgs-unfree input (recommended) 2. User's `~/.config/nixpkgs/config.nix` 3. `NIXPKGS_ALLOW_UNFREE=1 nix develop --impure` ### Duplicate Nixpkgs Downloads Use `follows` to chain inputs to a single nixpkgs source. ### Overlay Not Applied Ensure overlay is in the `overlays` list when importing nixpkgs: ```nix pkgs = import nixpkgs { inherit system; overlays = [ my-overlay.overlays.default ]; }; ``` ### Hash Mismatch Re-fetch with `nix-prefetch-url` and update the hash. Hashes change when upstream updates binaries at the same URL.