Flakes Have Failed
Over the years Nix adoption has grown dramatically, and, with it, sharing of Nix projects has grown as well. This need to consume one project from another began with ad-hoc solutions of passing various instances of evaluated projects as arguments. Eventually, the Nix world realized that it should be treating these dependencies just like any other dependency in Nix: declaratively and reproducibly. However, legacy default.nix
and shell.nix
files, which are operated on by nix-build
and nix-shell
respectively, did not lend themselves to this paradigm. A new solution that no longer relied on impure nix-channel
usage and was capable of locking inputs as well as exporting a standard interface was needed.
Nix Flakes were, and are, a proposed solution for resolving these two separate issues that the Nix ecosystem experiences when sharing projects. Firstly, projects needed to no longer rely on system-mutable state such as nix-channel
and NIX_PATH
. To accomplish this, all Flake evaluation is, by default, restricted to pure evaluation mode. In this mode no access to any impure system state is allowed. This includes channels from nix-channel
and NIX_PATH
as well as reading environment variables. Secondly, dependencies of Nix projects should be saved within the project in a format capable of reproducing those dependencies on any system. Finally, a standardized schema exists to declare well-known project exports.
Despite continued efforts to the contrary, Nix Flakes have systematically failed within the Nix ecosystem. Their presence as a core implementation detail of Nix is frequently contested, the feature remains experimental with no clear timeline of becoming stable within the next decade, and any changes to the structure of Flakes are discouraged regardless of its experimental status or myriad flaws. Flakes have not meaningfully improved in several years, “crucial” fixes and features for Flakes have stalled in open pull requests headed almost entirely by Determinate Systems, and the feature itself has been both declared unstable as well as stable by the Nix development team and Determinate Systems respectively. This absurdity is entirely self-inflicted and can be resolved by any party at any time, but participants choose otherwise.
Inputs are a core component of the Flake model. These represent other Nix Flakes or arbitrary archives which may be fetched to use within the project. Therein lies a fundamental limitation of Flakes: the only thing they are capable of loading is Flakes. All legacy projects must be loaded as raw archives and imported manually. Flakes are first-class, everything else is a distant second regardless of its Nix usage. This incompatibility introduces many needlessly tedious or even frustrating experiences in the name of “simplicity”. In fact, Nixpkgs itself must frequently be imported manually despite providing a Flake due to Nix Flakes simply not providing the flexibility that the singular most core project to Nix (aside from Nix itself) requires to function. How Flakes were designed to be incompatible with Nixpkgs remains a confounding question.
Considering Flakes are first-class within this new model, one would expect the ability to configure or otherwise interact with the settings of a particular flake to allow for customized usage of a project. No such feature exists within Flakes and is not planned to exist. This limitation restricts Flakes to a thin surface area, only allowing a project to expose attributes on an attribute set. While Nix Flakes do provide the ability to replace a dependency's input with another one, this functionality is not adequate for anything other than a minor version bump of a transient input. Nix projects regularly require additional configuration, the most common examples including override
and overrideAttrs
from Nixpkgs packages, yet Flakes remain static. This alone makes Flakes diametrically opposed to the way that Nix projects are composed and used.
The lack of extensibility is felt deeply when stepping outside of the blessed well-known schema. This problem has been acknowledged by Determinate Systems in the form of Flake Schemas, but to this day no solution is proposed for Nix to resolve this shortcoming. Currently, Flake outputs remain an ad-hoc attribute set with mere suggestions rather than rules around what may be exposed and where. No type checking is done to ensure that the exposed values are acceptable. No solution exists for specifying non-standard exports in such a way that Nix can understand them. It is no surprise that Nix projects have only used Flakes for their thin veneer of structure while all other logic and configuration is done separately. The ever-growing list of Flake abstraction libraries is further proof of its many shortcomings in this regard.
Nix Flakes also, for some odd reason, establish additional evaluation restrictions while happily doling out footguns to users. Flake entry files, flake.nix
, have their inputs and settings evaluated statically. This forces their definitions to be written in a subset of the Nix language, lacking variables, functions, imports, and anything one would expect to be useful in a programming language. Given this it is strange that Flakes make users write inputs at all rather than just storing them in a JSON file in the same way that Nix does for flake.lock
. All the while restricting evaluation, Flakes allow users to easily and conveniently give any Flake project root access to their machine via Flake settings which alter Nix's own configuration. A single mistake is all that is needed for a Nix Flake to attack your machine.
Given the severe flaws of Flakes and the refusal to recognize many or all of them, it is not clear that Nix Flakes will ever be usable. This feature will not be released in a stable state and whatever state it is in is already one of failure. There is no longer a reason for the Nix ecosystem to participate in this charade. Nix Flakes do not solve the problems that many had hoped they would. In fact, they cause many more problems in their implementation. It is time to move on, Flakes have failed.