NOTE: This is an experimental version of Nix. Things might break.
Nix Flakes is an experimental branch of the Nix project that adds dependency management and a central entry-point to Nix projects.
Here are some notes that I took for myself on the subject.
A non-strict history of things that are happening. Latest in front.
nix eval <INSTALLABLE> --start-repl-on-eval-errors
. e5662banix bundle nixpkgs#jq
which can be used to build installable bundles. #3880nix diff-closures <drv1> <drv2>
that shows package version changes between two profiles. [#3818](https://github.com/NixOS/nix/pull/3818_Add the following options to the NixOS configuration:
{ pkgs, ... }:
{
# Install the flakes edition
nix.package = pkgs.nixFlakes;
# Enable the nix 2.0 CLI and flakes support feature-flags
nix.extraOptions = ''
experimental-features = nix-command flakes
'';
}
Then run nixos-rebuild switch
and that’s it.
Run nix-env -iA nixFlakes
to replace Nix with the flakes edition.
Edit either ~/.config/nix/nix.conf
or /etc/nix/nix.conf
and add:
experimental-features = nix-command flakes
This is needed to expose the Nix 2.0 CLI and flakes support that are hidden behind feature-flags.
Finally, if the Nix installation is in multi-user mode, don’t forget to restart the nix-daemon.
NOTE: flake makes a strong assumption that the folder is a git repository. It doesn’t work outside of them.
In your repo, run nix flake init
to generate the flake.nix
file. Then run
git add flake.nix
to add it to the git staging area, otherwise nix will not
recognize that the file exists.
TODO: add more usage examples here.
See also https://www.tweag.io/blog/2020-05-25-flakes/
The flake.nix
file is a Nix file but that has special restrictions (more on
that later).
It has 3 top-level attributes:
description
which is self…describingnixConfig
allows to set per-project Nix configuration.input
is an attribute set of all the dependencies of the flake. The schema
is described below.output
is a function of one argument that takes an attribute set of all
the realized inputs, and outputs another attribute set which schema is
described below.Eg (from the commit message):
{
nixConfig.bash-prompt-suffix = "ngi# ";
nixConfig.substituters = [ "https://cache.ngi0.nixos.org/" ];
}
This is not a complete schema but should be enough to get you started:
{
inputs.bar = {
# Source of the input. It supports `github:` `gitlab:` and a number of
# other schemes
url = "github:foo/bar/branch";
# Turn off if the target is not a flake.
flake = false;
# Used to override inputs of the target if it is a flake.
inputs = {
# For example, here we declare to use the same version as the parent
# nixpkgs. It's probably also possible to override the URL attribute.
nixpkgs.follows = "nixpkgs";
};
};
}
The bar
input is then passes to the
Here is what I found out while reading src/nix/flake.cc
in CmdFlakeCheck
.
Where:
<system>
is something like “x86_64-linux”.<machine>
is something like “mymachine”.<attr>
is an attribute name like “hello”.<job>
is a hydra job name like “release”.<flake>
is a flake name like “nixpkgS”.<store-path>
is a /nix/store.. path{ self, ... }@inputs:
{
# Executed by `nix flake check`
checks."<system>"."<attr>" = derivation;
# Executed by `nix build .#<name>`
packages."<system>"."<attr>" = derivation;
# Executed by `nix build .`
defaultPackage."<system>" = derivation;
# Executed by `nix run .#<name>
apps."<system>"."<attr>" = {
type = "app";
program = "<store-path>";
};
defaultApp."<system>" = { type = "app"; program = "..."; };
# TODO: Not sure how it's being used
legacyPackages = TODO;
# TODO: Not sure how it's being used
overlay = final: prev: { };
# TODO: Same idea as overlay but several.
overlays."<attr>" = final: prev: { };
# TODO: Not sure how it's being used
nixosModule = TODO;
# TODO: Same idea as nixosModule but several
nixosModules."<attr>" = TODO;
# TODO: Not sure how it's being used
nixosConfigurations."<machine>" = TODO;
# TODO: Similar idea as for nixosModules but for hydra jobs.
hydraJobs."<job>" = TODO;
# Used by `nix flake init -t <flake>`
defaultTemplate = {
path = "<store-path>";
description = "template description goes here?";
};
# Used by `nix flake init -t <flake>#<attr>`
templates."<attr>" = { path = "<store-path>"; description = ""; );
}
See also:
There is a special, undocumented way to build NixOS configurations with flakes.
First, change flake.nix
to output a configuration. This uses the
nixosConfigurations
key. The nixpkgs
flake includes a helper for that:
{
outputs = { nixpkgs, ... }: {
nixosConfigurations.mymachine = nixpkgs.lib.nixosSystem {
modules = [
# Point this to your original configuration.
./machines/mymachine/configuration.nix
];
# Select the target system here.
system = "x86_64-linux";
};
};
}
Then to switch configurations, use nixos-rebuild --flake .#mymachine switch
,
from the same repository where the flake.nix
file is located.
To switch a remote configuration, use:
nixos-rebuild --flake .#mymachine \
--target-host mymachine-hostname --build-host localhost \
switch
NOTE: Remote building seems to be broken at the moment, which is why the build host is set to “localhost”.
One of the nix feature of the Flake edition is that Nix evaluations are cached.
Let’s say that your project has a shell.nix
file that looks like this:
$ shell.nix as nix
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
mkShell {
buildInputs = [
nixpkgs-fmt
];
shellHook = ''
# ...
'';
}
Running nix-shell
can be a bit slow and take 1-3 seconds.
Now create a flake.nix
file in the same repository:
$ flake.nix as nix
{
description = "my project description";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem
(system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
devShell = import ./shell.nix { inherit pkgs; };
}
);
}
Run git add flake.nix
so that Nix recognizes it.
And finally, run nix develop
. This is what replaces the old nix-shell
invocation.
Exit and run again, this command should now be super fast.
NOTE: TODO: there is an alternative version where the
defaultPackage
is apkgs.buildEnv
that contains all the dependencies. And thennix shell
is used to open the environment.
Assuming that the flake defines a devShell
output attribute and that you are
using direnv. Here is how to replace the old
use nix
stdlib function with the faster flake version:
$ use_flake.sh as sh
use_flake() {
watch_file flake.nix
watch_file flake.lock
eval "$(nix print-dev-env)"
}
Copy this in ~/.config/direnv/lib/use_flake.sh
or in
~/.config/direnv/direnvrc
With this in place, you can now replace the use nix
invocation in the
.envrc
file with use flake
.
The nice thing about this approach is that evaluation is cached.
Nix Flakes has a Nix evaluation caching mechanism. Is it possible to expose that somehow to automatically trigger direnv reloads?
With the previous solution, direnv would only reload iff the flake.nix or flake.lock files have changed. This is not completely precise as the flake.nix file might import other files in the repository.
See https://github.com/numtide/nix-flakes-installer#github-actions
Flake inputs can also be cached in the Nix binary cache!
nix flake archive --json \
| jq -r '.path,(.inputs|to_entries[].value.path)' \
| cachix push $cache_name
When in the repository top-level, run nix build .#<attr>
. It will look in
the legacyPackages
and packages
output attributes for the corresponding
derivation.
Eg, in nixpkgs:
nix build .#hello
Traditionally we would run nix-build ci.nix
or something equivalent but
flakes only support pointing nix build
to a single derivation.
I am not 100% confident on this answer: it looks like exposing the derivation
in the checks
output attribute, and then running nix flake check
does the
trick.
Flakes only takes files into account if they are either in the git tree.
You don’t necessarily have to commit the files, adding them in the git staging
area with git add
is enough.
Because the evaluation in Flakes is “pure”, a few things are disabled.
Pure evaluation can also be enabled by using --option pure-eval true
on
standard nix CLIs. Eg:
$ nix-instantiate --option pure-eval true --eval --expr '(builtins.currentTime)'
error: --- EvalError -------------------------------------------------------------- nix-instantiate
at: (1:2) from string
1| (builtins.currentTime)
| ^
attribute 'currentTime' missing
To find these out I searched for evalSettings.pureEval
in the
“src/libexpr” folder of the Nix repo.
All these builtins are not defined in pure evaluation:
builtins.currentTime -> int
builtins.currentSystem -> str
Some more special behaviours:
builtins.getEnv str -> str
returns empty strings.builtins.storePath
throws '__storePath' is not allowed in pure evaluation mode
builtins.filterSource
and builtins.path
has some condition in it, I
don’t know exactly which.builtins.fetchTree
also has some conditions.<foo>
throws cannot look up '<foo>' in pure evaluation mode (use'--impure' to override)
Because Flakes are pure by default, something like nix run nixpkgs#steam
will complain that it’s unfree, even if NIXPKGS_ALLOW_UNFREE=1
is set in the
environment.
The workaround is to disable the pure evaluation with the --impure
flag like
so:
NIXPKGS_ALLOW_UNFREE=1 nix run --impure nixpkgs#steam
_____ < EOF > ----- \ (\/) \ (_o | / | \ \______ \ )o /|----- | \| /|