This repository uses the Nix package manager to declaratively define two things:
- A personal user environment (dotfiles repo) — tools always available on the machine
- A benchmarking environment (research-project repo) — tools available only when doing benchmarking work
The key benefit: anyone can reproduce the exact same environment on any Linux machine with Nix installed, regardless of what OS is running.
Traditional package managers like apt or brew are imperative and implicit — you run commands over time and the system ends up in some state that is hard to reproduce. If you set up a machine six months ago you probably cannot remember exactly what you installed or which versions you used.
Nix is declarative and explicit — you describe what you want in a file, and Nix produces exactly that. The same file on a different machine produces an identical environment, including exact package versions. This is especially useful when:
- Moving a benchmarking setup from a Raspberry Pi to an HPC cluster
- Onboarding a collaborator who needs the exact same toolchain
- Reproducing results on a different machine months later
dotfiles/
├── flake.nix # entry point, pins exact versions of everything
├── flake.lock # auto-generated lock file, do not edit manually
├── home.nix # top-level user config, lists installed packages
└── bash.nix # shell config, aliases, direnv integration
research-project/
├── flake.nix # defines the benchmarking shell environment
├── flake.lock # pins exact versions
└── .envrc # tells direnv to auto-activate the environment
The entry point for Nix. It has two parts:
inputs — declares where packages come from and pins them to a specific revision of nixpkgs (the Nix package repository, which contains over 100,000 packages).
outputs — declares what this flake produces. In the dotfiles repo this is a homeConfigurations output (a user environment). In the research-project repo this is a devShells output (a temporary shell with benchmarking tools).
Auto-generated by Nix the first time you run any nix command. It records the exact git commit hash of every input, ensuring that running the flake today produces the same result as running it in six months. Always commit this file.
Declares which packages are always available in the user's environment. This is analogous to a list of apt install commands, but declarative — removing a package from this file and running home-manager switch will uninstall it.
home.packages = with pkgs; [
git
neovim
tmux
bat # better cat
eza # better ls
];Configures the bash shell declaratively, including:
- direnv — automatically activates the benchmarking environment when you
cdinto the project directory - zoxide — smarter
cdthat learns your most visited directories - shellAliases — replaces common tools with better alternatives (
cat→bat,ls→eza)
Defines the benchmarking shell environment. Running nix develop inside this directory gives you a shell with exactly these tools at exactly these versions:
packages = with pkgs; [
jdk21
gradle
perf
linuxPackages.cpupower
];The prompt changes to [Bench-Marking-Shell] when the environment is active so you always know which context you are in.
-
Install Nix:
sh <(curl -L https://nixos.org/nix/install) --daemon -
Enable flakes (add to
/etc/nix/nix.conf):experimental-features = nix-flakes nix-command -
Clone the dotfiles repo and activate:
git clone <repo> ~/dotfiles nix run home-manager -- switch --flake ~/dotfiles#elias
-
Open a new shell session. All tools from
home.nixare now available.
If you have set up the dotfiles (which includes direnv), entering the project directory is enough:
cd ~/research-project
# environment activates automatically
gradle jmhOr manually without direnv:
cd ~/research-project
nix develop# update all inputs to their latest versions
nix flake update
# re-activate to apply changes
home-manager switch --flake ~/dotfiles#elias # for dotfiles
nix develop # for research-projectBecause everything is pinned in flake.lock, porting is just:
- Install Nix and enable flakes (same as above)
- Clone the repo
- Run
nix develop
The benchmarking environment on the Spark machine will be bit-for-bit identical to the one on the Raspberry Pi — same JDK version, same Gradle version, same everything. No "works on my machine" problems.
If the Spark machine uses a different CPU architecture (e.g. x86_64 instead of aarch64), the system variable in flake.nix needs to match. This is the only change required.