Working with Overlays
Learn how to customize and modify packages using Nix overlays.
Table of Contents
Section titled “Table of Contents”- Overview
- What is an Overlay?
- Creating Overlays
- Common Use Cases
- Advanced Techniques
- Testing Overlays
- Best Practices
- Examples
Overview
Section titled “Overview”Overlays allow you to modify or extend the nixpkgs package set. They:
- Override package versions
- Add custom packages
- Apply patches to existing packages
- Customize build options
What is an Overlay?
Section titled “What is an Overlay?”Overlay Function
Section titled “Overlay Function”An overlay is a function that takes two package sets and returns modifications:
final: prev: { # final = resulting package set # prev = original package set
mypackage = prev.mypackage.override { ... };}
How Overlays Work
Section titled “How Overlays Work”nixpkgs (original) │ ▼overlay 1 (modifications) │ ▼overlay 2 (more modifications) │ ▼final package set (your config uses this)
Each overlay can reference:
final
- The final package set (after all overlays)prev
- The previous package set (before this overlay)
Creating Overlays
Section titled “Creating Overlays”Basic Overlay Structure
Section titled “Basic Overlay Structure”Create nix/overlays/my-overlay.nix
:
final: prev: { # Add new package my-tool = prev.callPackage ./packages/my-tool.nix { };
# Override existing package ripgrep = prev.ripgrep.overrideAttrs (old: { version = "14.0.0"; });}
Apply Overlay in Flake
Section titled “Apply Overlay in Flake”In flake.nix
:
{ darwinConfigurations.your-mac = darwin.lib.darwinSystem { modules = [ { nixpkgs.overlays = [ (import ./nix/overlays/my-overlay.nix) (import ./nix/overlays/another-overlay.nix) ]; } # ... other modules ]; };}
Apply in Module
Section titled “Apply in Module”In a module file:
{ config, pkgs, ... }:{ nixpkgs.overlays = [ (final: prev: { # Overlay content }) ];}
Common Use Cases
Section titled “Common Use Cases”Override Package Version
Section titled “Override Package Version”final: prev: { # Use different version nodejs = prev.nodejs_20;
# Override with specific version python3 = prev.python311;
# Use unstable version neovim = final.callPackage ./custom-neovim.nix { };}
Apply Patches
Section titled “Apply Patches”final: prev: { myapp = prev.myapp.overrideAttrs (old: { patches = (old.patches or [ ]) ++ [ ./fix-bug.patch (prev.fetchpatch { url = "https://github.com/user/repo/pull/123.patch"; sha256 = "sha256-..."; }) ]; });}
Change Build Options
Section titled “Change Build Options”final: prev: { # Enable feature vim = prev.vim.override { python3Support = true; rubySupport = true; };
# Disable feature git = prev.git.override { guiSupport = false; svnSupport = false; };
# Custom configuration nginx = prev.nginx.override { modules = [ prev.nginxModules.rtmp ]; };}
Add Custom Package
Section titled “Add Custom Package”final: prev: { my-tool = prev.stdenv.mkDerivation { pname = "my-tool"; version = "1.0.0";
src = prev.fetchFromGitHub { owner = "user"; repo = "my-tool"; rev = "v1.0.0"; sha256 = "sha256-..."; };
buildInputs = [ prev.go ];
buildPhase = '' go build -o my-tool '';
installPhase = '' mkdir -p $out/bin cp my-tool $out/bin/ ''; };}
Advanced Techniques
Section titled “Advanced Techniques”Python Package Overlays
Section titled “Python Package Overlays”final: prev: { python3 = prev.python3.override { packageOverrides = pyfinal: pyprev: { # Add custom Python package my-python-lib = pyfinal.buildPythonPackage { pname = "my-python-lib"; version = "1.0.0"; src = prev.fetchPypi { pname = "my-python-lib"; version = "1.0.0"; sha256 = "sha256-..."; }; };
# Override Python package version requests = pyprev.requests.overridePythonAttrs (old: { version = "2.31.0"; }); }; };}
Recursive Overlays
Section titled “Recursive Overlays”final: prev: { # Reference other overlay modifications my-app = final.callPackage ./my-app.nix { # Use modified version from another overlay nodejs = final.nodejs; };}
Conditional Overlays
Section titled “Conditional Overlays”final: prev:let isDarwin = prev.stdenv.isDarwin;in{ # macOS-specific override myapp = prev.myapp.overrideAttrs (old: { buildInputs = old.buildInputs ++ prev.lib.optionals isDarwin [ prev.darwin.apple_sdk.frameworks.Security ]; });}
Overlay Composition
Section titled “Overlay Composition”[ (import ./ledger-agent.nix) (import ./ai-tools.nix) (import ./custom-packages.nix)]
# In flake.nixnixpkgs.overlays = import ./nix/overlays;
Testing Overlays
Section titled “Testing Overlays”Build with Overlay
Section titled “Build with Overlay”# Test buildnix build .#your-package
# Check package derivationnix show-derivation .#your-package
# Evaluate packagenix eval .#your-package.version
Debug Overlay
Section titled “Debug Overlay”final: prev: { # Add debug output myapp = prev.myapp.overrideAttrs (old: { # Print during build postBuild = '' echo "Building with version: ${old.version}" ${old.postBuild or ""} ''; });}
Test in Isolation
Section titled “Test in Isolation”# Test single package with overlaynix-build -E 'with import <nixpkgs> { overlays = [ (import ./my-overlay.nix) ]; }; mypackage'
Best Practices
Section titled “Best Practices”Use Final for References
Section titled “Use Final for References”# ✅ Good: Use final to reference overlaid packagesfinal: prev: { my-app = final.callPackage ./my-app.nix { nodejs = final.nodejs; # Gets overlaid version };}
# ❌ Bad: Use prev, might get old versionfinal: prev: { my-app = final.callPackage ./my-app.nix { nodejs = prev.nodejs; # Gets pre-overlay version };}
Minimize Rebuilds
Section titled “Minimize Rebuilds”# ✅ Good: Only override what's neededfinal: prev: { myapp = prev.myapp.override { enableFeature = true; };}
# ❌ Bad: Causes full rebuildfinal: prev: { myapp = prev.myapp.overrideAttrs (old: { # Changing derivation forces rebuild of dependents version = old.version; });}
Organize Overlays
Section titled “Organize Overlays”# ✅ Good: Separate concernsnix/overlays/├── ledger-agent.nix # Ledger-specific├── python-packages.nix # Python overrides├── security-tools.nix # Security packages
# ❌ Bad: Everything in one filenix/overlays/└── everything.nix # 1000+ lines
Document Changes
Section titled “Document Changes”final: prev: { # Override Node.js version to 20 for compatibility with our tools # See: https://github.com/org/tool/issues/123 nodejs = prev.nodejs_20;
# Add patch to fix memory leak # Upstream PR: https://github.com/upstream/repo/pull/456 myapp = prev.myapp.overrideAttrs (old: { patches = [ ./fix-memory-leak.patch ]; });}
Examples
Section titled “Examples”Example 1: Ledger Agent Overlay
Section titled “Example 1: Ledger Agent Overlay”From your config (nix/overlays/ledger-agent.nix
):
final: prev: { ledger-agent = prev.python3Packages.trezor-agent.overridePythonAttrs (old: { pname = "ledger-agent";
propagatedBuildInputs = old.propagatedBuildInputs ++ [ prev.python3Packages.ledger-bitcoin prev.python3Packages.hidapi ];
postInstall = '' ${old.postInstall or ""} # Custom ledger-specific binaries for tool in ledger-agent ledger-gpg-agent; do ln -s $out/bin/trezor-agent $out/bin/$tool done ''; });}
Example 2: AI CLI Tools
Section titled “Example 2: AI CLI Tools”From your config (nix/packages/ai-clis.nix
):
{ pkgs, lib }:
pkgs.buildNpmPackage { pname = "ai-clis"; version = "1.0.0";
src = ../../tools/js;
npmDepsHash = "sha256-aSip2gUeEM3RRCszF8jvH9MX0X3dRbLW5C4dy+lsdpE=";
dontNpmBuild = true;
postInstall = '' mkdir -p $out/bin if [ -d "$out/lib/node_modules/ai-cli-bundle/node_modules/.bin" ]; then for bin in "$out/lib/node_modules/ai-cli-bundle/node_modules/.bin"/*; do if [ -f "$bin" ] || [ -L "$bin" ]; then ln -s "$bin" "$out/bin/$(basename "$bin")" fi done fi '';
meta = with lib; { description = "Bundle of pinned npm CLIs (Claude Code, MCP Inspector)"; platforms = platforms.darwin ++ platforms.linux; };}
Example 3: Custom Python Tools
Section titled “Example 3: Custom Python Tools”final: prev: { my-python-tools = prev.python3.withPackages (ps: [ # Standard packages ps.requests ps.flask
# Custom package (ps.buildPythonPackage { pname = "my-lib"; version = "1.0.0"; src = prev.fetchPypi { pname = "my-lib"; version = "1.0.0"; sha256 = "sha256-..."; }; propagatedBuildInputs = [ ps.requests ]; }) ]);}
Example 4: Security Tools Override
Section titled “Example 4: Security Tools Override”final: prev: { # Use latest nmap nmap = prev.nmap.overrideAttrs (old: { version = "7.94"; src = prev.fetchurl { url = "https://nmap.org/dist/nmap-7.94.tar.bz2"; sha256 = "sha256-..."; }; });
# Add Ledger support to GnuPG gnupg = prev.gnupg.override { enableLdap = true; enableUsb = true; };}
Example 5: Development Tools
Section titled “Example 5: Development Tools”final: prev: { # Latest neovim with plugins neovim-custom = prev.neovim.override { configure = { packages.myPlugins = with prev.vimPlugins; { start = [ nvim-lspconfig nvim-treesitter ]; }; customRC = '' lua << EOF require('lspconfig').nixd.setup{} EOF ''; }; };
# Go with custom version go = prev.go_1_21;
# Node.js 20 LTS nodejs = prev.nodejs_20;}
Troubleshooting
Section titled “Troubleshooting”Overlay Not Applied
Section titled “Overlay Not Applied”# Check if overlay is loadednix eval .#darwinConfigurations.your-mac.config.nixpkgs.overlays
# Check package after overlaynix eval .#darwinConfigurations.your-mac.pkgs.mypackage.version
Infinite Recursion
Section titled “Infinite Recursion”# ❌ Bad: Creates infinite loopfinal: prev: { mypackage = final.mypackage.override { ... }; # Refers to itself!}
# ✅ Good: Use prevfinal: prev: { mypackage = prev.mypackage.override { ... };}
Build Failures
Section titled “Build Failures”# Build with tracenix build .#mypackage --show-trace
# Check derivationnix show-derivation .#mypackage
# Compare with originalnix show-derivation nixpkgs#mypackage
Next Steps
Section titled “Next Steps”- Adding Packages - Install software
- Creating Modules - Write custom modules
- Testing Builds - Test your changes
- Packaging Example - Complete example
Related Documentation
Section titled “Related Documentation”- Structure Guide - Overlay system explained
- Nix Fundamentals - Understanding Nix
- Troubleshooting - Common issues
External References
Section titled “External References”- Nixpkgs Overlays - Official docs
- Overlays Wiki - Community docs
- Package Overriding - Override techniques
Ready to customize packages? Start with a basic overlay!