I would like to implement a new feature to nix/NixOS, and want to brainstorm the idea with you folks ![]()
Problem:
I’m running NixOS outside of its comfort zone: On multiple VPS with 1GB RAM and 10GB storage. This is only possible by
- Using Remotebuilder → Some packages take too much space when being build.
- nixpkgs.url = github:NixOS/nixpkgs/master and updating hourly → when there are too many changes at once, the disk will run full while rebuilding
- doing hourly nix-garbage-collect -g → Otherwise the disk is full in matters of hours
- Having tweaked the store as much as possible (Hard-Linking, etc)
nix.settings.auto-optimise-store = true;nix.settings.min-free = "${toString (100 * 1024 * 1024)}";nix.settings.max-free = "${toString (1024 * 1024 * 1024)}";nix.settings.max-jobs = 0;
- Using Impermanence to have some “auto clean” on startup
- Move Logfiles off the system very aggressively (hourly logrotate)
- having /nix/store on a compressed btrfs filesystem
- (Some optimizations I added in the past year and I’m probably forgetting)
And still, sometimes the updates pile up and I have to change my config to remove all “non-vital” packages, rebuild the system in the minimal configuration, (reboot and reclaim the space by garbage collection) and then add the “non-vital” packages back.
Idea
Instead of building the whole derivation at once, I would like to make partial derivations. So when an upgrade is to be built and it would for example include upgrades to bigPackageA and bigPackageB, the regular nixos-rebuild would fail with “disk full”, since there is not enough space for four bigPackages (i.e bigPackageA.old, bigPackageA.new, bigPackageB.old, bigPackageB.new)
I would like the new upgrade mechanism to:
- try building the systemDerivation using the regular nixos-rebuild → If that works, I rather prefer the old and tested.
- If the previous step was not successful due to a lack of storage space, I would like to build a new systemDerivation, which is in between my old derivation and the new one, only upgrading one package and masking the other package versions to their old versions. If the build of new derivation is successful, then the old one may be discarded from the nix store. Repeat until the target systemDerivation is reached.
- When an error happens during the build, return to the initial systemDerivation building each previous systemDerivation until we’re back to the initial state. Somewhat like this:
InitialState → build derivationWithA → build was successful, cleaning up old A → derivationWithAB → build was successful, cleaning up old B → build derivationWithABC (Errors) → clean up → build derivationA → build initialState
The builds would still be reproducible while having a smaller storage footprint.
Implementation
- First, I believe I need some way of tagging a package/service as “non-vital”, so that the new upgrade mechanism knows, which packages can be removed, without the update system breaking. The tagging should not be a problem.
- Also, I think having both of the build trees (currentState, targetState) would be beneficial. I will need to make small alterations to the currentState tree and then feed it back in to nix. These alterations will be using the targetStates tree and changing the hashes of the parents node.
- Pulling it all together would be a script that would automate the tree manipulation and nix calling parts.
Questions
- Do you think, this is a feasable Project? Are there technical reasons that make this impossible? Is there a logical fallacy I stumbled into?
- Where could I start getting the trees from?
- I was able to get the abstract syntax tree (AST) from a Nix-expression by using nix-instantiate, but its only for one file and not for the whole project.
- I poked around in github:nixos/nix, looking for good entry points where I could hook into, but have found none.
Disclaimer
I’m aware that the whole idea of running NixOS on tiny machines is a bit rediculous, or straight stupid. I don’t advise anyone to do such a thing, let alone in production.
But I believe that this feature might become handy for someone, somewhere, someday.