Understanding the Flakes Input System
Understanding the Flakes Input System
Two humans, two nixpkgs, two glibcs. The boring default is: inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05", follows so nested flakes share it, and flake.lock committed.
Mental model
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
home-manager = {
url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
};| Piece | Job |
|---|---|
url |
Where to fetch (github, git+ssh, path, tarball) |
follows |
Reuse this flake’s input instead of nested default |
flake.lock |
rev + narHash for every input |
nix flake update moves every input. nix flake update nixpkgs moves one. Do not update everything on Friday without flake check.
master / nixpkgs-unstable is not a pin you can name in an incident. 26.05 is. Home Manager’s train is release-26.05, not master.
Worked examples
Case 1: follows in the tree
Save as flake.nix:
# flake.nix
{
description = "Desk infrastructure flake with shared nixpkgs";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
home-manager = {
url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager }: {
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt-rfc-style;
};
}flake-utils is optional sugar; skip it unless you already depend on it.
nix flake lock
nix flake metadataOutput (shape):
Inputs:
├───home-manager: github:nix-community/home-manager/…
│ └───nixpkgs follows input 'nixpkgs'
└───nixpkgs: github:NixOS/nixpkgs/nixos-26.05/…
follows input 'nixpkgs' is the line you want. Without it, home-manager may lock a second nixpkgs.
Case 2: One-input update
nix flake update home-managerOutput (shape):
• Updated input 'home-manager'
nixpkgs did not move. That is the point. A lock PR that also bumps sops, disko, and nixpkgs is three incidents in one.
Case 3: Audit the lock
nix flake metadata --json | jq -r '.locks.nodes.nixpkgs.locked.rev'
nix flake metadata --json | jq -r '.locks.nodes.nixpkgs.locked.narHash'Put that rev in the PR description when you bump 26.05.
Case 4: path: for a sibling repo
# flake.nix fragment
{
inputs.desk-tooling.url = "path:../desk-tooling";
inputs.desk-tooling.inputs.nixpkgs.follows = "nixpkgs";
}path: is not copied to a cache the same way as git+ssh. Fine on a laptop. CI should use a git URL + lock (git+ssh://git.desk.internal/desk-tooling.git?ref=refs/tags/…).
Case 5: Check after a bump
nix flake update nixpkgs
nix flake check -LIf check fails, git checkout -- flake.lock and try a smaller step. The lock is git; treat it as code.
git diff --stat flake.lockIf flake.lock is uncommitted, there is no team.
When you add sops / disko / home-manager, give each a follows:
# flake.nix fragment
{
inputs.sops-nix.url = "github:Mic92/sops-nix";
inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs";
inputs.disko.url = "github:nix-community/disko";
inputs.disko.inputs.nixpkgs.follows = "nixpkgs";
}nix flake metadata | grep -c 'nixpkgs:' One nixpkgs: line (plus follows children). Three unlocked nixpkgs nodes is the eval-RAM incident.
The trap
The trap is three copies of nixpkgs because nobody wrote follows. Eval RAM and download time explode. nix flake metadata every time you add an input.
The other trap is not committing flake.lock. CI then evals whatever nixos-26.05 means this morning.
The boring rule
nixos-26.05(and matchingrelease-26.05for HM). Notmaster.inputs.<dep>.inputs.nixpkgs.follows = "nixpkgs";- Commit the lock. Update one input per PR when you can.
metadatato see follows. Count nixpkgs nodes: one.path:locally; git URLs in CI.
Try this
nix flake metadataon this flake; count nixpkgs nodes. Should be one.- Remove
follows,nix flake lock --update-input home-manager, metadata again, putfollowsback. nix flake update home-managerandgit diff flake.lock.jqthe lockednarHashfor nixpkgs; paste it in a dummy PR description.