Iām relatively new to NixOS and currently running a Flakes + Home Manager setup. Iām absolutely loving the declarative approach so far, but Iāve run into an issue that is completely destroying my bandwidth and SSD.
Every time I do my weekly routine (nix flake update followed by updating both the system and Home Manager), the total update size is massive. Iām talking about ~10GB of downloads and around 28GB unpacked (and yes, this size includes both the system and Home Manager updates combined)!
While investigating, I noticed that Nix is frequently downloading the exact same version of a package but with different hashes. For example, matugen-3.0.0 gets downloaded twice:
Is there a flaw in how Iām handling the stable and unstable inputs that causes this dependency tree split (resulting in different hashes for the same app)?
How can I prevent Nix from doing this?
Or is this massive download size just the reality of mixing unstable packages and custom kernels, and I should just move heavy GUI apps to Flatpak via declarative modules?
Any advice, explanations, or pointers to fix my config would be hugely appreciated. Thanks in advance!
Yes, mixing unstable and stable will lead to duplicate packages. If only a single dependency is different, itās a different package to nix even if the version number is the same.
I think @truh is off the mark here. Unstable does mean more churn, and mixing it with stable means more absolute disk space use, but it shouldnāt cause large downloads or rebuilds.
Something is going very wrong, and itās (probably) not the multi-nixpkgs you depend on. You can test for that by just aliasing the two nixpkgs inputs into one.
Youāre also using flake-parts but not even doing the one thing most people use it for (not having to type x86_64-linux once or twice). It looks like you over-engineered without understanding why youāre over-engineering.
Itās hard to spot exactly whatās going wrong because this over-engineering obfuscates what youāre doing, but this kind of thing is a huge indicator of nixpkgs repo imports being done poorly, especially in the context of NixOS (and flake-parts and flakes). Still, nixā lazyness should save you, so all I can imagine is that somehow you made something really low-level depend on something that updates frequently through an overlay.
Iād recommend bulldozing that flake.nix and rewriting things from scratch without a framework and only for one specific host.
You can attempt hyper-generalizing once you understand whatās going on a bit better, or just donāt generalize at all; Most desktop configurations will not be applied to more than one or two hosts (you seem to have exactly one, given that youāre enumerating exactly one file), what you have looks like itās extremely overkill and therefore needlessly complex.
Alternatively, try to give us a minimum reproduction scenario, though doing so will likely already show you what youāre doing wrong.
FWIW, one issue with flakes is that every time you make a change to a Git-backed nix config, it must copy the entire source of your config to the nix store instead of using the current Git hash. This only happens when you have uncommitted files in the working dir (you can git add the files to avoid this). This is what āGit tree /etc/nixos is dirtyā warning means when you invoke nixos rebuild. Other configuration derivations may rely on the source hash, so you may see things being rebuilt in this circumstances, mostly configuration derivations.
This typically wonāt cause double copies of most packages as youāre seeing but itās worth knowing.
No, it also happens for git repositories with committed files. Every time you make any change to your repo, though only once for each commit or uncommitted change, since the hashes will match if the files are the same. This is āneededā for pure eval, and one of the biggest issues with flakes in general.
And yes, itās unrelated for the specific question asked here.
Thanks for the blunt feedback @TLATER, you were actually spot on about the poor repo imports and making low-level stuff depend on unstable.
I took a deep dive into my config and found exactly where the āover-engineeringā went wrong. The fatal mistake was instantiating a completely independent pkgs-unstable = import inputs.nixpkgs-unstable {} and blindly passing it through specialArgs to over 20 different modules across my system. It was dragging two parallel universes of stdenv, glibc, and rustPlatform into my builds, inflating my system closure size to a massive 30.9 GiB and causing those 10GB weekly downloads.
I didnāt quite bulldoze the entire flake.nix from scratch, but I did a massive architectural cleanup:
Nuked pkgs-unstable from specialArgs entirely: Itās no longer being passed globally to my modules.
Reverted to Stable: Shifted ~90% of my packages (basic CLI tools, security wrappers, GNOME) back to pure stable pkgs.
Quarantined Unstable via Overlay: For the few apps that actually need to be bleeding edge (like gemini-cli or ollama), I set up a proper overlay in flake.nix (pkgs.unstable = import ...) so they are explicitly called via the pkgs namespace without forcing a split in the foundational dependency tree. I also used final.callPackage cherry-picking for specific GUI/Rust apps to build them against my stable foundation.
Thanks for pointing out the bad importsāit forced me to look at how I was injecting dependencies!
Those imports are redundant to begin with due to flake-parts and NixOS/home-manager. I do still recommend bulldozing, and learning each module system step by step.