Tutorial · Derivations
Tutorial: deterministic derivations
Nix hashes a build's declared inputs. A builder can still read the current time or generate
a random UUID, so rebuilding that derivation can produce different bytes.
inRewind runs the builder in a VM and puts the VM's settings and runtime in the
derivation. The build repeats with those inputs fixed.
The reproducible builds tutorial varies the environment to find dependencies you can remove, such as an unsorted directory listing. Here we pin the environment so an unchanged program repeats. Sorting the listing makes the build agree across directory orders; pinning the VM picks one order. Both are useful. A race still needs fixing even if one pinned schedule happens to pass.
Set up the builder
Install Rewind as the Nix tutorial describes. Nix needs sandboxing
enabled and the kvm system feature, with /dev/kvm available to
builds. It also needs a feature naming the builder's CPU vendor. On NixOS:
programs.rewind.systemFeature = "amd";
On another distribution, add extra-system-features = kvm rewind-amd to
nix.conf. Use intel and rewind-intel on Intel. The
examples below were recorded on AMD and use clock = "exits", which needs no
branch counter. The reference explains the clock options
and machine settings.
A build that stamps itself
stamp compiles a UUID and a timestamp into its executable. Each ordinary build reads a new UUID and the current time:
$ nix run github:fzakaria/rewindvm#stamp
...
build b2060565-ce20-4c7b-80f9-e19f540dbc78 at 2026-11-09T18:06:35Z
$ nix build --no-link github:fzakaria/rewindvm#stamp --rebuild
...
checking outputs of '/nix/store/...-stamp-0.1.0.drv'...
error: derivation '/nix/store/...-stamp-0.1.0.drv' may not be deterministic: output "/nix/store/...-stamp-0.1.0" differs
--rebuild builds the same derivation again and compares the result with the
stored output. The bytes differ.
Wrap the derivation
Save this as stamp.nix, changing vendor to
"intel" on an Intel builder:
let
rewind = builtins.getFlake "github:fzakaria/rewindvm";
inherit (rewind.lib.x86_64-linux) inRewind;
in
inRewind {
epoch = 1790985600;
clock = "exits";
vendor = "amd";
} rewind.packages.x86_64-linux.stamp
inRewind takes the machine settings and a derivation. Nix runs that
derivation's builder inside Rewind and receives its outputs as usual. The
epoch sets the VM's clock at boot to midnight on 2026-10-03.
$ nix build --no-link --print-out-paths --impure -f stamp.nix
...
/nix/store/2va0vhvj2ksl5kf66hyjmshprn19cv8k-stamp-0.1.0
$ $(nix build --no-link --print-out-paths --impure -f stamp.nix)/bin/stamp
...
build 4ce9bd9b-a9df-4eb0-968c-cae21737b552 at 2026-10-03T00:00:00Z
Now rebuild it:
$ nix build -L --no-link --impure -f stamp.nix --rebuild
...
checking outputs of '/nix/store/...-stamp-0.1.0.drv'...
...
stamp> gcc -O1 -Wall -DBUILD_ID='"4ce9bd9b-a9df-4eb0-968c-cae21737b552"' -DBUILT_AT='"2026-10-03T00:00:00Z"' -o stamp stamp.c
...
stamp> rewind: run a5453754435731c0 exited:0 after 4296 steps, 0.021s virtual, 0.605s wall (poweroff)
--rebuild passes. The program still reads the clock and the kernel's random
pool, but both builds see the same values. You can replace stamp with your
own derivation without changing its builder.
The result's hash covers the machine settings in rewindFlags and the Rewind
package in rewindRuntime, including its kernel and initrd. Changing any of
them gives Nix a different recipe and output path. In a project, pin the Rewind flake
input in flake.lock as you pin nixpkgs.
Change the clock
Change the epoch to midnight on 2025-06-15:
$ sed -i 's/1790985600/1749945600/' stamp.nix
$ nix build --no-link --print-out-paths --impure -f stamp.nix
...
/nix/store/ibl83iyrplb8i1p152fh57535faw8zm3-stamp-0.1.0
$ $(nix build --no-link --print-out-paths --impure -f stamp.nix)/bin/stamp
...
build 9332c115-745f-4d61-b6e3-95f52d97852b at 2025-06-15T00:00:00Z
The new output has its own store path and timestamp. Rebuilding the original recipe still produces the original timestamp.
Costs and limits
The VM has one virtual CPU. Build threads take turns on that CPU, so builds that benefit
from many cores will take longer. Each build also packs its input closure into an image.
The build directory and outputs live in guest memory; increase mem for large
builds.
Builds have no network. Keep fixed-output derivations, such as source fetches, outside
inRewind. AMD and Intel are separate build targets, and upgrading the pinned
Rewind package rebuilds the derivations that use it.
The Nix build reference covers all machine settings,
exporting files from a recording, and onRewind, which uses Nix's experimental
external-builder interface.
The engine is free and MIT licensed. The desktop app is $49 personal or $99 per seat.