Skip to content

Overlay

Overview

An overlay is a function final: prev: { ... } that returns attribute overrides and new packages to merge into nixpkgs's fixed-point package set.

For fixed-point mechanics, stacking order, and extends, see Overlays Pattern. For install paths and longer how-tos, see Writing Overlays.

Details

Naming (final / prev vs self / super)

An overlay is a two-argument function. Newer code uses final and prev; older code often uses self and super. The roles are the same either way:

Argument Legacy name Meaning
final self The composed package set after this overlay and all later overlays—the fixed point
prev super The set from nixpkgs and overlays before this one

Use prev for the package you are replacing and for helpers already on the previous stage (callPackage, fetchFromGitHub, …). Use final for dependencies of packages you define or override so downstream code in the composed set sees the updated attrs. Swapping the two is a common footgun; see Overlays Pattern.

Composition order

Nixpkgs evaluates overlays left to right in the list. Each overlay extends the result of the previous ones. If two overlays both set the same top-level name, the later one wins. Put foundational pins (interpreters, BLAS/LAPACK providers, stdenv tweaks) before overlays that depend on those attrs via final.

import <nixpkgs> {
  overlays = [
    (final: prev: { python3 = prev.python312; })          # runs first
    (final: prev: { myApp = prev.myApp.override {        # runs second;
      python3 = final.python3;                            # sees pinned python3
    }; })
  ];
}

Under the hood, layers compose with lib.composeManyExtensions and lib.extends; details are in Overlays Pattern.

Shallow merge pitfall

Overlay results merge with shallow //, not deep recursion. A later overlay that sets a top-level attr replaces the entire value from an earlier overlay—nested keys are not merged.

# overlay 1
final: prev: { foo = { a = 1; version = "1.0"; }; }

# overlay 2 — replaces foo entirely; `a` is gone
final: prev: { foo = { b = 2; }; }
# composed top-level foo => { b = 2; } only

To keep nested keys, merge manually (prev.python3.pkgs // { myPkg = ...; }) or override a specific nested attr instead of replacing the whole parent set. This is the main structural footgun when stacking overlays.

Where to apply

Mechanism Scope / notes
import nixpkgs { overlays = [ ... ]; } Project or one-off import; path-based overlay lookup is skipped when overlays is passed
pkgs.extend / pkgs.appendOverlays Recompute the fixed point on an existing pkgs; costly—prefer overlays at import time
NixOS nixpkgs.overlays System evaluation only; does not affect standalone nix-env or ad-hoc import <nixpkgs> unless you share the same list
~/.config/nixpkgs/overlays.nix User list of overlays (used when overlays is not passed to import)
~/.config/nixpkgs/overlays/ Directory of .nix files (lexicographic order); error if both this and overlays.nix exist
<nixpkgs-overlays> on NIX_PATH Highest-priority path lookup when overlays is not passed

Reuse one overlay list for NixOS and user config when you want nix-build, nix-shell, and the system to stay aligned. Full install paths and legacy packageOverrides are covered in Writing Overlays.

Overlays vs overrides

An overlay is set-level: it returns a fragment of pkgs that nixpkgs folds into the fixed point, so anything resolving through final.someAttr picks up your change. .override and .overrideAttrs are package-level: they adjust one derivation's function arguments or mkDerivation attrs and return a single new derivation—nothing else in pkgs changes unless you wire that value in.

Overlays often call .override / .overrideAttrs on prev packages to propagate a tweak set-wide (for example, swapping a BLAS provider or patching hello everywhere). See Overlay vs Override.

This page stays at the concept layer—overlay shape, naming, shallow merge, and common attachment points. Per-package overrides, fixed-point internals, and install-path how-tos are covered in the pages linked under See also.

Examples

To pin Python across the set:

final: prev: {
  python3 = prev.python312;
}

Patching one package inside an overlay (same shape as ../meta/examples/overlay-snippet.nix):

final: prev: {
  hello = prev.hello.overrideAttrs (old: {
    pname = old.pname + "-patched";
  });
}

At import time, pass a list to overlays:

import <nixpkgs> {
  overlays = [
    (final: prev: { myTool = prev.callPackage ./my-tool.nix { }; })
  ];
}

When a new package needs attrs from the composed set, pull them from final:

final: prev: {
  myCli = prev.callPackage ./tools/my-cli.nix {
    inherit (final) lib openssl python3;
  };
}

References

See also