Show HN: Nix Snapshotter – Native understanding of Nix packages for containerd (github.com)
We built this because Nix is a great fit for making efficient containers. They don't need an OS because Nix captures all dependencies exactly. However, the current process of creating Nix images is subpar because one needs to transform Nix packages into a format that container runtimes understand.
Using nix-snapshotter, instead of downloading image layers, packages come directly from the Nix store. Packages can be fetched from a binary cache or built on the fly if necessary. All existing non-Nix images continue to be supported, and Nix layers can be interleaved with normal layers.
nix-snapshotter also provides a CRI image service, which allows Kubernetes to resolve image manifests from Nix directly too. This enables for the first time, fully declarative Kubernetes resources, all the way down to the image specification and its contents. With this, you can even run pure Nix images without a Docker Registry at all, if you wish.
We'd love for you to try it out, there is a one-liner for Nix users to boot a VM with everything pre-configured: https://github.com/pdtpartners/nix-snapshotter
34 comments
[ 2.1 ms ] story [ 79.0 ms ] thread[1] https://nixery.dev/
With `pkgs.nix-snapshotter.buildImage`, containerd natively understands Nix packages, so everything is pulled at package granularity without the layer limit. This means all container content is either already in your host nix store or fetched from your Nix binary cache. Think delta image pulls, which isn't possible with regular images since one bit difference will change the layer hash and thus duplicate.
That said, nothing stops Nixery from building nix-snapshotter images. Then we'll have an Docker Registry that dynamically builds native Nix images.
The same is true if you are using the special image reference like `nix:0/nix/store/f8b1hia3hcqwa5d46anzy3cszi3s6ybk-nix-image-redis.tar`, it just means that instead of fetching the image manifest from a Registry, you are using the Nix protocols.
You will only build from source dynamically (and a mix of caching) if you are doing `kubectl apply -f ${podSpec}` as a full deployment Nix expression. See the README's asciinema and this file as an example: https://github.com/pdtpartners/nix-snapshotter/blob/v0.1.0/e...
Awesome project!
0 is actually an unbindable port, so nix:0 is just an arbitrary string that is unlikely to have conflicts. The Kubelet needs to be configured with —image-service-endpoint to use the nix-snapshotter gRPC socket to handle the “PullImage” RPC. There we can take over resolving the image reference when it’s prefixed with nix:0.
Do you mean using a Dockerfile to run a Nix package build, embedding the resulting files (and transitive dependencies) in the image file? If so, this objectively superior in several ways: 1) only one copy of any package exists on disk: the one in the Nix store, instead of multiple images having independent copies. 2) Related to this de-duplication, it will be faster to go from pristine host (virtual machine, dev machine, whatever) to having containers up and running, as you no longer have bulky images to transfer over the network. 3) I haven’t looked at the finer details, but I’m pretty sure this means that the host can build the required packages once, meaning better caching — instead of multiple Dockerfiles rebuilding the same packages, the host builds once and all dependent containers get use the host’s Nix store, again without any copying of files involved.
Edit:
From hinshun’s comment above, point #3 is incorrect — either the host’s Nix store would need a copy already present, or the package would need to be available in a remote store/cache. Even with that correction, you still avoid redundant package builds — either the package is available remotely/locally, or the image fetch fails — so things are still only ever built once :)
We already have rootless containerd and nix-snapshotter. And I believe there is work underway to run rootless nix (and rootless k3s too). You may be able to run the whole thing unprivileged one day.
Nix is a fantastic tool for building isolated work artifacts, but not the greatest for actually scheduling work across a fleet of machines.
This combines both worlds, I hope this succeeds and gains adoption!
In terms of image size, since it’s dependencies are explicit (all the way down to glibc and lower), the Nix image is comparable to a scratch image with a single statically compiled binary.
Comparing to the existing Nix built images, we are moving image build performance from O(n) (n is number of Nix packages) to ~O(1) because the build process becomes just constructing a JSON referring to already built Nix packages.
Of course, it understandably requires the host VM to be NixOS, which means my Kubernetes cluster needs to be running NixOS, which means I would need to abandon the ease afforded by EKS/GKE... which is a tall ask. It would be great if there was a way to more easily add this into popular managed Kubernetes distributions, to play around more with running "Nix images", simply by adding nodeSelector and/or node affinity to the Kubernetes manifests.
I have tested with EKS, and you should be able to use nix-snapshotter with GKE too. May be able to put together some docs for that later.
In the meantime, see this for running a different snapshotter on EKS: https://blog.realvarez.com/using-estargz-to-reduce-container...
I'm a k8s novice, but would there be a way to run the nix daemon/cache in a semi-persistent pod on each node, and then "attach" it to the actual worker pods?
I got the impression that it uses the node's nix daemon.
From the project readme's FAQ https://github.com/pdtpartners/nix-snapshotter#faq
> What's the difference between this and a nix-in-docker?
> If you run nix inside a container (e.g. nixos/nix or nixpkgs/nix-flake) then you are indeed fetching packages using the Nix store. However, each container will have its own Nix store instead of de-duplicating at the host level.
> nix-snapshotter is intended to live on the host system (sibling to containerd and/or kubelet) so that multiple containers running different images can share the underlying packages from the same Nix store.
I didn’t mean running nix-snapshotter as a Kubernetes resource because then there’s a chicken & egg problem. Kubernetes needs nix-snapshotter image service to resolve the nix-snapshotter image.
I dont see a killer selling point, just some middle ware with extra containerization features which also could be pulled into nixpkgs.
But i have to admit, in my perception bubble VC gets more desperate and to them, the search metrices for nix must be shining like a christmas tree. I am certainly biased negatively.
This is addressed in the readme FAQ. https://github.com/pdtpartners/nix-snapshotter#faq
If you're running Nix-based images on Kubernetes, seems like this tool has reduced overhead / storage compared to if you build an OCI/Docker image.