Hi, I am not sure this is the right place, but I wanted to mention there is no documentation on how to force nix-channel to update all channels even when the channels have been recently updated. There is also no documentation on how long the channel cache lasts for before being updated, or that channels are cached in the first place (at least on the nix-channel documentation page).
One use case worth documenting for is perhaps you have a development branch channel that you want to consume. You add a commit to test how it would install/run on a NixOS system, then try to run sudo nix-channel --update. However, you have recently run that command, so the channel is not updated and sudo nixos-rebuild does nothing.
If that is not a use case for nix-channel, then maybe that should be documented as well, pointing to the proper tool.
Anyway the workaround I found was to run these commands:
sudo nix-channel --rollback # optionally add number of arbitrarily old enough generation
sudo nix-channel --update # this now works
If there is a better way to dogfood nix packages in a non-flake nix system config Iâd love to know. This is how I tested this package properly installed and run (as a new contributor), and also how I applied patches like this one to test before contributing them to upstream LegendsViewer-Next.
As an aside, is there a reason why channels are cached without checking that refs havenât been updated? Is it a bandwidth reduction issue?
Hi @donttellmetonottellu, have you tried setting --tarball-ttl 0? I havenât used nix-channel in a long time, so all documentation there amounts to manual testing and code archeology (writing extra tests for such cases is often not worth the effort, the framework is rather cumbersome to use).
Itâs a common misconception that itâs âflakes vs channelsâ. Flakes are about multiple things, source management is just one of them, and nix-channel with search paths is not the only way to do that in stable Nix:
The confusion for NixOS users stems more from the fact that nixos-rebuild obscures the evaluation entry point and thus strongly nudges you to inject source references at the call site or some other side channel rather than specifying them in the Nix expression that invokes your configuration. The flakes variant simply doesnât do that, but that has nothing to do with flakes per se (except thereâs no other way with flakes, because they are mandatory pure).
Thanks, this is what I needed. I had forgotten about that option.
Thatâs the problem, I donât want it to be that pure. I want to be able to imperatively say fetch the latest updates for my system. I also donât like encouraging a culture of I donât update my system because updates may break my configuration, especially on stable (which is where I am). I thought that was one thing nix was supposed to solve!
/siderant
I am not against using flakes. I think they are very useful for dev environments, or reproducible dev builds, etc. I just donât want anything to do with my system configuration potentially relying on something that I forgot and now doesnât get security (or any) updates, and eventually breaks due to the impure ways packages communicate with each other and the outside world (which nix will never solve). It is also extremely useful to have one command be the one true source of truth when it comes to the current input state of your system, rather than having to hunt through potentially dozens of flake.lockâs (or figure out how to unify them somehow). I already spent enough time figuring out a workflow for editing my system configuration (because by default that is very difficult to do and setup with version control). I donât need flakes to complicate things further.
Also, it seems that nix-channel (and nixos-rebuild --update-all) is the only way to get updates on your system like a regular Linux distribution. This is an essential feature of a self-respecting non-LFS Linux distribution, not âlegacy codeâ. I would not use NixOS as my daily driver if I had no easy way to update my system with one command. Nixâs biggest problem is accessibility to new users, removing this in some future version or allowing it to bit rot will only close the door on any potential new users without esoteric use cases. Reproducible systems should be the standard, not a weird quirk. And NixOS will lose a race it is already ahead in against e.g. Ubuntu (Core) if they make simple things harder for new users. Updates always have been and always will be by-default imperative (or even non-interactive). Even if you built your system using only flakes (somehow), only the truly insane would use declarative programming by manually editing flake.lockâs: instead using nix flake update, which is both imperative and impure. A feature should not be progressed towards if it realistically has little real world use cases, and conversely a feature that is needed and common should be well documented and maintained. Otherwise whatâs the point of NixOS other than as a reference distribution (like KDE Neon).
I think the nix definition of âpureâ has strayed from the original definition of pure functions (according to Wikipedia):
the function has no side effects (no mutation of non-local variables, mutable reference arguments or input/output streams).
Of course, we must note that I/O streams in this case are part of the purity, due to the architecture of NixOS, and are not a required part of âpurityâ (or at least Iâll allow it, otherwise nix is fundamentally âimpureâ).
I donât see channels as causing impurity or irreproducibility. You could control those âargumentsâ to get the same, reproducible result, although there is a lack of tooling to make that easy. It is true that they arenât defined by âparametersâ in the same sense that arguments to { ... }: are, but rather more similar to how modules work they are like library imports (mod rust_module; or import defaultExport from "module-name";, or using System;. The lack of such features hasnât historically defined a âfunctional programming languageâ. It is assumed that if you update Haskell (or a Haskell library) you may get a different result in the same pure function, which may or may not be considered a bug depending on the project goals and semantic versioning. The claim of pure functional languages is not that the same code will do the same thing through all versions of the language, rather that on a specific version the code will do the same thing all the time. And even if it was insisted that the channels are non-local variables, they are not mutated. And the system is reproducible, given the same system channels.
That is not to say that mitigating the effects of language updates is a bad thing, I recognize the effort to create a translational layer for nix that is used in derivatives, and think it is great that nix 2.1 does the same thing with the same inputs as nix 2.99 (hopefully). But the âstandard libraryâ (nixpkgs) is a whole different animal, and has no such guarantees.
It would have been nice if flakes had leveraged the existing way that nix functions worked, instead of creating a new DSL to (learn and) work with that is incompatible with nix-channel. Then instead of being a hacky attribute set with inputs and outputs, a flake.nix could be fed inputs in the same way as a configuration.nix, and be a proper nix function instead (and have the ability to define imports), with the same predefined inputs determined by the flake.lock. Channels could be both defined by a flake.lock and by a systemâs global channels. nix channel would be a new subcommand that would work with both system and flake channels. Instead of everyone using flake-utils, a nixpkgs argument would have built-in tools to enable system-specific configurations, and in general the standard library would be more complete for flake usage. Then we would have all the same features as core nix, in flakes. It would feel more nix-like, less people would be upset and confused, and everything would be rainbows and sunshine. LOL. Oh well. History is history.
Finally, there is no such thing as a pure development shell, which is what flakes are used for quite often (and what I use it for). You cannot guarantee that you wonât have an âit works on my systemâ problem if you ever allow that use case. Because of that, development flakes are no more pure than a shell.nix.
Not at all, and no one would ever make such a guarantee. Nix is meant to ensure that all the dependencies are specified correctly, but itâs not going to handle data migration or configuration migration for your NixOS system.
Software upstream of NixOS changes, we would never guarantee that your config from today will work 6 years from now unless you use the exact same nixpkgs revision in both cases. (And even then, some software sources might disappear making some packages unbuildable.)
nix flake update && sudo nixos-rebuild switch
(or boot and reboot, whichâd be safer in some cases).
Thatâs the whole reason to not use channels. Although flakes get you the same inputs, and nixpkgs sort of half-tries with reproducible builds, claiming reproducible deployments on a desktop system would be nonsensical, unless itâs a highly controlled environment where all the data can be wiped (e.g. school workstations). So letâs be clear that in the kinds of use cases most of the casual users are talking about, NixOS will never have reproducible deployments, as thereâs too much outside of NixOSâs control.
But you fundamentally canât even approach reproducibility of any useful kind without declarative systems. Thatâs why nix-channel sucks.
Not at all, the input is declared with your config, thatâs how lockfiles work in programming languages. If youâre looking for semver, thatâd be the one thing nix is missing.
Yeah, âpureâ devshells are stupid, the point of using flakes here would only be to ensure itâs the same nixpkgs revision used for both development and build/deployment.
I appreciate your respectful response, as it is useful for learning a flake userâs perspective. I do have a few things to respond to.
On stable, changes should not be breaking. And plenty of distros make that guarantee, less security fixes, on stable releases. I only have to worry about breaking changes twice a year, which is great (and has been true for the 3 releases Iâve been through). Of course, the stability guarantee is not actually controlled directly by the language, but rather maintainer policies and testing. However the systems in place do a good job of rarely breaking things that donât break themselves from not being updated. Apparently using stable makes you the weird one, though.
I was that guy that used Debian because he didnât like being spammed with non-security updates.
I really wish there was a nixos-stable branch (not channel, that wouldnât make sense), just so that it would be possible to get that stability guarantee on flake updates (less the twice-a-year releases) without having the issue of breaking nix flake update when the current stable is depreciated.
No, I agree, but thatâs why I use stable. Nobody seems to appreciate the fact that stable is, well, stable! It solves the problem that pinning a specific version of nixos-unstable does, without locking you in a specific version and potentially being a security problem. Of course, stable is only maintained for ~7 months, but that is fine, assuming there is an easy way to update to the next stable (which there isnât really). You can deal with breaking changes twice a year at most and thatâs not a huge problem. I actually quite love the fact that stable releases twice a year, as opposed to Debianâs once every two years⊠which is not really viable for a desktop. I do think occasional LTS releases would be a good idea, if only we had the funding.
The problem I had, as a new nix user, is that I started with a NixOS system. I didnât try the package manager first, I just dove right in. So whenever I looked up how to do something, as flakes are not first class citizens in NixOS, I always ignored the âflake versionâ and was extremely frustrated when there was no traditional configuration in the examples. The reason for this, was that I was already rawdogging the NixOS system, and spending hours finding solutions that would involve one web search and command on Debian/Manjaro/OpenSUSE, so I wasnât interested in even trying to figure out how to use flakes at all. They looked like an extension to the language, not a required part, and the syntax was already esoteric enough without adding flakesâ magic attributes and DSL. And trying to use the flake version of every package would probably require dozens of hours of prerequisite learning.
As it is, I still have absolutely no idea how a âflake systemâ would even work, and thatâs after over a year of using NixOS as my daily driver. That command means nothing to me because I donât know how to hook the flakes up in the first place.
Thatâs why I want a nix channel command with those quirks ironed out. Channels do not need to be limited to channels.nixos.org. In fact, I donât only use channels.nixos.org. I use home-manager and a custom nixpkgs for testing. You could pull directly from the git repository, or even a specific git commit. And since the channels are mirrored in the git repository, you could reproduce a personâs entire system from the channels, if only nix-channel provided a way to get the specific git revisions and simply print them out. channels.nixos.org already provides that information, ex: https://releases.nixos.org/nixos/25.05/nixos-25.05.802746.7282cb574e06/git-revision.
The command itself is not pure, because it does not follow the first point in the definition. If you run nix flake update, you will get different lockfiles at different times.
Sure, the flake itself, including the lockfile, might be pure: but not the command that creates the lock. That is the point I am making about channels, they have the potential to be just as reproducible as flakes. All youâd have to do is create a command like nix channel lock export to export a channels.lock and nix channel lock import for importing the lockfile. The information for rebuilding the channels is already in the nixpkgs repo. You could also add the lock output to the nix-info command. And if there is a backwards-compatible way to introduce a feature, why not? Why completely reinvent the wheel?
What I am looking at, from an engineering/architectural standpoint, is that I donât see how a domain specific language was necessary in the first place (it is probably bad practice to reach for a DSL first when you have control of the language, especially from the perspective of language servers). I think that these features should have been properly RFCâd and introduced into core nix as optional features in a backwards-compatible way, instead of creating a walled garden where flakes are completely incompatible with regular nix expressions. And also perpetually âexperimentalâ because of the controversy. It was frustrating for me as a new nix user, and it still frustrates me now, even though I at least somewhat understand the flake syntax now (I still havenât made much use of them).
Also, the main use I have for flakes is reproducible (cross-)compilation, but I havenât had much luck figuring that out.
which hopefully will be simplified in a future npins release. This allows me to follow the 25.05 branch and enjoy stability and reproducibility without needing all of flakes, while having a nice nom-enabled update workflow.
Well, I wasnât a nix user when âthe pastâ happened. So I donât really care all that much. Iâm fine with things as they are, but being able to criticize the current system without starting a flame war is important for the health of any project.
Iâm not anti-flake, I am writing one now (or trying anyway). Regardless of if it could have been done better or not, it exists, and is the current best way AFAIK to build packages outside of nixpkgs.
I like the idea of nh, I already implemented my own shell scripts to use nom under the hood lol!
Back on topicâŠ
The original problem brought up in this thread was the lack of documentation of tarball caching in key legacy nix commands, such as nix-channel and nixos-rebuild. Iâd simply like a mention in the command documentation that updates are not guaranteed to happen without --tarball-ttl 0. nix-channel is still the default in NixOS, so this should be documented for new users.
And they arenât, except for security fixes and honest mistakes. Never claimed otherwise. Not sure what magic you expect from the language, in every distro stability is a promise kept via maintainer policies.
Contradictory with the prior point, unless we introduced a separate subcommand to nixos-rebuild to cross release boundaries.
nix flake update is just âfetch me the latest update that matches this refspecâ modulo tarball-ttl. Thatâs not fundamentally a flakes problem though nor do I see why thatâs a weakness of flakes. An update command being âpureâ makes no sense because youâre fetching something outside your code to get the latest updates. So no kidding, the update command is impure, in every language imaginable.
Sure you could design a lockfile that doesnât require a flake.nix, and itâs been done with npins and whatnot. In fact if I could get past my c++ skill issue and patch (n/l)ix to use pure eval (not the copying-to-store nonsense but some saner mechanism that only looks at the files withon your project) outside of a flakes context, I would 100% do so.
Pull request with a change to this file appreciated! Ping me for a review.
If I understand your desired user experience correctly, you may want to try the following setup. It will always build the latest version of the stable release you specify. This code is tested and should work as is with your configuration.nix.
Assuming you use Bash or Zsh, try it out with $(nix-build -A run-vm). When youâre happy, no need to run nixos-rebuild, just do $(nix-build -A nixos) switch.
let
pkgs = import (fetchTarball "channel:nixos-25.05") { };
in
rec {
eval = pkgs.nixos ./configuration.nix;
nixos = pkgs.writeShellApplication {
name = "switch";
result = eval.config.system.build.toplevel;
text = ''
nix-env -p /nix/var/nix/profiles/system --set "$result"
exec "$result"/bin/switch-to-configuration "$@"
'';
derivationArgs = {
inherit postCheck;
};
};
run-vm = pkgs.writeShellApplication {
name = "run-vm";
text = ''
# let QEMU create the disk image in memory
cd "$(mktemp -d)"
# always clean up
trap 'rm -f nixos.qcow2' EXIT
"${pkgs.lib.getExe eval.config.system.build.vm}" "$@"
# clean up the terminal, useful when running with `-nographic`
reset
'';
derivationArgs = {
inherit postCheck;
};
};
# this allows running e.g. `$(nix-build -A run-vm)` directly
postCheck = ''
mv $out/bin/$name /tmp/$name
rm -rf $out && mv /tmp/$name $out
'';
}
I donât expect any, my point was about reproducibility via pinning unstable vs reliability in using nixos-stablebranch and updating. I was just trying to explain that I prefer traditional maintainer-guaranteed stability over 100% stable but potentially insecure pinned unstable releases.
Everyone seems to have different definitions and preferences of stability/reproducibility/purity
As a flake target: it would be useful to know that updating your flake wonât usually randomly break, but only when you are updating across release boundaries. nixos-stable might not be a good name for it, maybe. But the idea is to have a branch that always points to the latest stable. Think how e.g. pkgs.winePackages.stable works. Maybe nix flake update or nixos-rebuild --update could warn and require user input if the underlying branch was changed, say from 25.05 â 25.11. IDK, itâs just annoying that NixOS and flakes donât have an easy way to say âupdate to the next stable releaseâ without manually editing the file. Which is not great when sharing flakes, because a consumer of a flake targeting stable written in 2023 would have to edit the inputs in 2025 to get the flake working again. Thatâs probably one reason why shared flakes almost always target unstable.
The point I wanted to make is not that it is uniquely a flakes problem/weakness, only that a lot of people seem to think that flakes solve that problem, and that flakes were the only way to solve it. nix-channel could easily have been (and still could be) updated to support importing and exporting lock files, and that would solve the system channel reproducibility problem without any backwards-incompatible additions.
What Iâd prefer is a universal lock file format that is supported (can optionally be used) by shell.nix, configuration.nix (through channels), and flake.nix, that is in upstream nix and is actually agreed on by everybody, maybe called something like nix.lock. Nobody is contesting (AFAIK) the usefulness of reproducible local builds.
The problem is that flakes represent an entirely new way of doing things, where perhaps the old way could have been (and should still be) improved upon, in a way that consumers of nix have a chance to posit their use cases (through RFCâs). The problem is that if a person decides not to use flakes to configure their system because they donât like the design/experimental status of flakes, they are just SOL when it comes to being able to share their exact configuration so someone else can rebuild it.
I think flakes are a great addition to nix, and should be in 3.0. But they are simply not a total replacement for the old way of doing things. The work that went into building the old tools shouldnât be wasted on the principle of ânewer is always betterâ. As long as people use the old tools they should be supported. And thatâs coming from a guy that likes shiny new things . I switched to nixos precisely because I was tired of things breaking and not being able to find documentation on how to fix it. Nix has mostly solved one of those problems for me.
It doesnât though; your proposed feature still inherently relies on system state. Youâve made it easier to share and reproduce that state, sure, but project evaluation itself is still tied to global system state and therefore not reproducible.
If you view NixOS entirely in a vacuum, and assume that there is nothing but the very system you are rebuilding, I can see how youâd arrive at that path (and that is where nix-channel undoubtedly comes from), but that thinking is precisely why channels fall short; itâs inherently global state.
If you want to build a VM or nixos-rebuild switch another system remotely, you would need to remember to first change your system state to match theirs, and then also remember to change it back. A design like that is deeply flawed, since multiple evaluations can give unexpected behavior, and the overall intent of what the state persists becomes meaningless; youâre just having a global variable for the sake of having a global variable.
I think you just havenât thought out the lockfile thing to its conclusion. Your skepticism of flakes is valid (and I believe @waffle8946 actually largely shares it), but the lockfile concept itself isnât really your issue.
To open your horizon a little, if you only ever care about one evaluation, you can think of nix flake update as the same as nix channel update, but it stores the channels in /etc/nixos instead of /nix/var, and therefore doesnât need import/export functionality in the first place.
Flakes obviously still come with a bunch of other baggage, and I think it is valid to not like that.
For this reason, I think npins still sound to me like exactly what you actually want. They are essentially just a standardization of what @fricklerhandwerk suggested, except the URL that is downloaded is read from a json file in /etc/nixos instead of a directory in /nix/var.
Standardization of that lockfile format to be shared between the flake world and npins world would be an interesting endeavour, but I donât think itâs really that useful, since you canât evaluate flakes without parsing flake.nix anyway, and the projects are cross-compatible even without such standardization already thanks to the load-flake builtin or whatever it was called. Fair enough @fricklerhandwerk, my point is still that cross-compat is already possible and not reliant on lockfile format standardization (itâs anyway just json).
???: you have made it easier to share and reproduce that state⊠therefore not reproducible.
That does not compute. It isnât a Schrödingerâs cat, it either is reproducible or isnât. The word you might be looking for is âpureâ. If it relies on global state, it isnât pure. My focus is that reproducibility is far more important than purity, and flakes have many circumstances where they are neither pure nor reproducible (read: development shells). Purity and reproducibility should be striven for, but it isnât always useful to have both. We have a command that is expected to be run when contributing/bug reporting in nixpkgs, and the reason for that is that there is no (technically vanilla) NixOS system in existence that is entirely pure without some ungodly flake black magic (and the obscurity of the black magic probably negates any positives purity might bring).
In the context of a flake.nix, it technically relies on âglobal stateâ as well. The flake.lock is not written in nix and is not directly referred to within flake.nix. So running nix build with a different lock with the same flake will produce different results. We get around that by defining the flake not as the file and the files it refers to itself, but as the directory that contains the flake, including all its members. I could easily make up some similar garbage definition of purity by saying that âmy Ubuntu system is reproducible, since if you have everything in / you will have the same system as meâ. Thatâs not what reproducibility/purity means, but it is if you take the flake definition of purity to its extreme.
Also, there are still bugs in how flakes interpret âthe current directoryâ, given that a flake takes git versioning into account in a not-so-sane (imo) way. I have had many issues in my development flow with flakes that I havenât bothered to report, including nix buildoverwriting my flake.lock because it wasnât version controlled. Insanity. Hair pulling ensues as I redownload a ton of dependencies on a terrible internet connection because I have no way of recovering the flake.lock that was totally clobbered. And this isnât ideal for the same reason it isnât ideal for a Cargo.lock to be clobbered just because it is gitignored.
Practically, what we are trying (I hope) to achieve is create a reliable and lightweight way of reproducing bugs and development environments. I donât think it particularly matters whether it is technically pure or not. Nobody is going to agree on what purity means. That is obvious. And what even is the point of purity beyond a certain point excepting âbut functional programming, broâ. But reproducibility? That is easily definable by ordinary means. I donât actually care about purity as long as another person can see the same thing I see with minimal effort. Right now, there is no single command that can reliably do this in a vanilla NixOS. That is why I am so frustrated with flakes. They are an addon that doesnât even solve the biggest problem.
Back to functional programming and purity. Here is an article I found, see especially section 5. Functional programming is great. But functional programming is a subset of computation. By definition, when you enforce purity, you are losing features. Most of nixâs scope and usefulness come from its impure parts. What parts are impure?
Well, IO is one. You canât install packages in a completely pure way. Theoretically, there could be a bug in how a FS is implemented that only shows up when files are in a certain order on disk. You could be on a COW system. You could be on a filesystem with limited path lengths. You could be on a filesystem without hard linking support. All of these things, nix tries to adapt to and eliminate problems with. But the reality is that the layer of nix that deals with these things is inherently impure. But removing that functionality would remove nixâs ability to be useful.
The user could make a mistake and edit part of the (technically still there) global state. I have (I know Iâm special) using only nix commands. This resulted in a borked system that had to be completely reinstalled from my config. Repair didnât work. So nix relies on its own global state being controlled strictly by itself and puts things in place to try to prevent users from editing that state: but nevertheless a bug within nix or malicious user action can remove that safety guarantee and result in a non-reproducible system: because nix isnât actually completely pure (/nix/var may as well be named /nix/globalstate, and this is still an issue with flakes or npins).
And no, npins is not what I want. Iâm interested in agreed upon community standards, which npins is not AFAIK. Removing the tarball-ttl does exactly what I want.
Honestly, Iâd be interested in learning how to have a flakes based system. As I said before, I am not anti-flakes. I just donât know how it would work, or if Iâd even like it (I would not like having more than one flake.lock). Because of how it was thrown into the nix ecosystem with all the care of a bull in a china shop, all the easily accessible documentation and the default NixOS images donât use flakes. Yes there is nix.dev, but it isnât exactly a light read (and you have to find it first). And according to nix.dev on flakes:
New Nix users were and still are encouraged by various individuals to adopt flakes despite there being no stability guarantees and no timeline to conclude the experiment.
This is a huge red flag to any new user of nix. It screams âdonât use this, you canât trust itâ. And why should I, when I get everything I want done without flakes (with the exception of better development shells that actually cache, and independent reproducible builds).
And, off tangent, the fact that 84% of people are using âexperimentalâ features in an ecosystem that favors reproducibility is insane. Even I have nix-command and flakes enabled globally
Edit: I forgot to mention that the entire nix system configuration is editable via an environment variable and it is easy to add this to a shell hook within any flake/shell.nix (or even a .zshrc)