Comparing_flakes_to_traditional_nix
Introduction: Flakes and Traditional Nix

- This post is based on notes from Nix-Hour #4, comparing Traditional Nix and Flakes, focusing on achieving pure build results. See the YouTube video for the original content. This guide adapts the information for clarity and ease of understanding.
What is Purity in Nix?
A key benefit of Nix Flakes is their default enforcement of pure evaluation.
In Nix, an impure operation depends on something outside its explicit inputs. Examples include:
- User’s system configuration
- Environment variables
- Current time
Impurity leads to unpredictable builds that may differ across systems or time.
Building a Simple “hello” Package: Flakes vs. Traditional Nix
- We’ll demonstrate building a basic “hello” package using both Flakes and Traditional Nix to highlight the differences in handling purity.
Using Nix Flakes
Setup:
mkdir hello && cd hello/
Create
flake.nix
(Initial Impure Example):# flake.nix { outputs = { self, nixpkgs }: { myHello = (import nixpkgs {}).hello; }; }
- Note: Flakes don’t have access to
builtins.currentSystem
directly.
- Note: Flakes don’t have access to
Impure Build (Fails):
nix build .#myHello
- This fails because Flakes enforce purity by default.
Force Impure Build:
nix build .#myHello --impure
Making the Flake Pure:
# flake.nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; in { packages.myHello = pkgs.hello; } ); }
flake-utils
simplifies making flakes system-agnostic and provides thesystem
attribute.
Pure Build (Success):
nix build .#myHello
Using Traditional Nix
Setup:
mkdir hello2 && cd hello2/
Create
default.nix
(Initial Impure Example):# default.nix { myHello = (import <nixpkgs> { }).hello; }
Build (Impure):
nix-build -A myHello
Impurity Explained:
nix repl nix-repl> <nixpkgs> /nix/var/nix/profiles/per-user/root/channels/nixos
<nixpkgs>
depends on the user’s environment (Nixpkgs channel), making it impure. Even with channels disabled, it relies on a specific Nixpkgs version in the store.
Achieving Purity: Using
fetchTarball
GitHub allows downloading repository snapshots at specific commits, crucial for reproducibility.
Get Nixpkgs Revision from
flake.lock
(from the Flake example):
# flake.lock "nixpkgs": { "locked": { "lastModified": 1746372124, "narHash": "sha256-n7W8Y6bL7mgHYW1vkXKi9zi/sV4UZqcBovICQu0rdNU=", "owner": "NixOS", "repo": "nixpkgs", "rev": "f5cbfa4dbbe026c155cf5a9204f3e9121d3a5fe0", "type": "github" },
Modify
default.nix
for Purity:# default.nix let nixpkgs = fetchTarball { url = "[https://github.com/NixOS/nixpkgs/archive/f5cbfa4dbbe026c155cf5a9204f3e9121d3a5fe0.tar.gz](https://github.com/NixOS/nixpkgs/archive/f5cbfa4dbbe026c155cf5a9204f3e9121d3a5fe0.tar.gz)"; sha256 = "0000000000000000000000000000000000000000000000000000"; # Placeholder }; in { myHello = (import nixpkgs {}).hello; }
- Replace
<nixpkgs>
withfetchTarball
and a specific revision. A placeholdersha256
is used initially.
- Replace
Build (Nix provides the correct
sha256
):nix-build -A myHello
Verification: Both Flake and Traditional Nix builds now produce the same output path.
Remaining Impurities in Traditional Nix:
- Default arguments to
import <nixpkgs> {}
can introduce impurity:overlays
:~/.config/nixpkgs/overlays
(user-specific)config
:~/.config/nixpkgs/config.nix
(user-specific)system
:builtins.currentSystem
(machine-specific)
- Default arguments to
Making Traditional Nix Fully Pure:
# default.nix {system ? builtins.currentSystem}: let nixpkgs = fetchTarball { url = "[https://github.com/NixOS/nixpkgs/archive/0243fb86a6f43e506b24b4c0533bd0b0de211c19.tar.gz](https://github.com/NixOS/nixpkgs/archive/0243fb86a6f43e506b24b4c0533bd0b0de211c19.tar.gz)"; sha256 = "1qvdbvdza7hsqhra0yg7xs252pr1q70nyrsdj6570qv66vq0fjnh"; }; in { myHello = (import nixpkgs { overlays = []; config = {}; inherit system; }).hello; }
- Override impure defaults for
overlays
,config
, and makesystem
an argument.
- Override impure defaults for
Building with a Specific System:
nix-build -A myHello --argstr system x86_64-linux
Pure Evaluation Mode in Traditional Nix:
nix-instantiate --eval --pure-eval --expr 'fetchGit { url = ./.; rev = "b4fe677e255c6f89c9a6fdd3ddd9319b0982b1ad"; }'
- Example of using
--pure-eval
.
nix-build --pure-eval --expr '(import (fetchGit { url = ./.; rev = "b4fe677e255c6f89c9a6fdd3ddd9319b0982b1ad"; }) { system = "x86_64-linux"; }).myHello'
- Building with a specific revision and system.
- Example of using
Updating Nixpkgs
nix flake update
nix build .#myHello --override-input nixpkgs github:NixOS/nixpkgs/nixos-24.11
Updating Traditional Nix (using niv
)
nix-shell -p niv
niv init
# default.nix
{ system ? builtins.currentSystem,
sources ? import nix/sources.nix,
nixpkgs ? sources.nixpkgs,
pkgs ? import nixpkgs {
overlays = [ ];
config = { };
inherit system;
}, }: {
myHello = pkgs.hello;
}
And build it with:
nix-build -A myHello
niv update nixpkgs --branch=nixos-unstable
nix-build -A myHello
Adding Home-Manager with Flakes
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
home-manager.url = "github:nix-community/home-manager";
};
outputs = { self, nixpkgs, flake-utils, home-manager, ... }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.myHello = pkgs.hello;
packages.x86_64-linux.homeManagerDocs =
home-manager.packages.x86_64-linux.docs-html;
});
}
nix flake update
nix flake show github:nix-community/home-manager
home-manager.inputs.follows = "nixpkgs";
Adding Home-Manager with Traditional Nix
niv add nix-community/home-manager
nix repl
nix-repl> s = import ./nix/sources.nix
nix-repl> s.home-manager
{ system ? builtins.currentSystem, sources ? import nix/sources.nix
, nixpkgs ? sources.nixpkgs, pkgs ? import nixpkgs {
overlays = [ ];
config = { };
inherit system;
}, }: {
homeManagerDocs = (import sources.home-manager { pkgs = pkgs; }).docs;
myHello = pkgs.hello;
}
nix-build -A homeManagerDocs