Can someone explain to me the advantage of using Nix over containers? What do they offer that are not provided with using docker or other container platform.
Strictly compared to containers, the big advantages are reproducibility and lower overhead.
Overhead: Windows and macOS can't run Linux-based containers natively. Instead, there's always a full Linux virtual machine running in the background acting as an intermediary and host for your containers. Nix can conjure arbitrary native development environments on a per-command or per-terminal basis, giving you all the performance of directly running tools without the risk of clashing with systemwide software.
Reproducibility: Nix provides much stronger guarantees about the exact versions of software you're running. It effectively gives you a lockfile for your entire dependency chain, all the way down to libc. Containers tend to be more stateful: everyone on your team may be using the same Dockerfile, but if you build an image from it two weeks apart, you're probably going to get very different outputs due to things like your apt-get update step returning new versions of packages. This doesn't happen with Nix.
The beauty is that this isn't either/or; you can actually use Nix to generate OCI container images which are thus fully specified and repeatable.
Adding to this, my understanding is that Nix does not guarantee any Nix derivation/package will seamlessly run on Linux, Mac, and Windows. So in this one aspect it is less capable than Docker, i.m.h.o.
> Nix does not guarantee any Nix derivation/package will seamlessly run on Linux, Mac, and Windows. So in this one aspect it is less capable than Docker, i.m.h.o.
Nix runs on Windows exactly like Docker runs on Windows— only inside a Linux VM. If you ship a Linux VM on Mac or Windows like people usually do for Docker, you're free to run the Linux version of a Nix package on those platforms.
Nix-Darwin doesn't do anything for packaging issues, it just offers an alternative module system for declaratively managing services and configurations in a NixOS-like way.
Docker containers also don't normally do any kind of service management, they're single-process images. They're not really comparable to orchestrated services managed by NixOS or Nix-Darwin modules. But if your concern is just shipping the same thing and you don't care about what's managing the services, a whole NixOS VM isn't any less efficient than some other VM in which you run `docker-compose` or Kubernetes.
That said, there are Nix-y ways to ship one or more supervised processes in a way that's portable across the platforms that Nix supports. One way is with a manually tailored supervisord config with a nix-shell or something like devenv.sh. Another strategy would be to leverage something an abstraction layer like this one: https://github.com/svanderburg/nix-processmgmt
Would nix guarantee that all upstreams are available forever? Is nix planning to replace all upstreams? (PyPI, Conda, npm etc?) OR does it plan to keep a cache forever?
Not yet. There have been some efforts around adding IPFS support to Nix, as well as making its storage fully content addressable, which would allow for peer-to-peer archival and distribution of source. https://blog.ipfs.tech/2020-09-08-nix-ipfs-milestone-1/
Nix doesn't keep a cache of the upstreams, though there are some projects planning to try to do that I think.
The build recipes pull from the original source and use a hash to ensure that the source artifacts don't change. Nix caching is usually done at the build output layer, e.g. the resulting binaries.
So it does plan to replace all up streams eventually including apt, npm, pypi, gem etc by providing the ability to build and configure all software? I am not understanding this, currently the original maintainers release on their own stores. the build and configure steps may change over time. Unless the original maintainers start building for nix, would not this be too hard to maintain?
> the build and configure steps may change over time. Unless the original maintainers start building for nix, would not this be too hard to maintain?
This is indeed very hard, but Nix's deep locking makes this a tractable problem: if a version of a package builds once, it will keep building for as long as the source code is available.
So in some ways, this Nix has an easier job compared to traditional distribution mechanisms. On the other hand, Nix can be sufficiently weird and unwieldy as to limit the number of potential contributors.
One difference is that Docker containers use a separate file system isolated from the host, so you have to separately install your editor/shell in there, mount/clone your dotfiles, etc. With a Nix-based development environment, it can add/override the tools you need, but you get to keep your shell customizations, editor config, etc.
Also reproducibility; it can be achieved with containers if you save the artifact (the container image), but that's not what people do in practice, they save only the recipe (the Dockerfile), and if you execute it tomorrow, it will produce a different result than today, and it will likely not even run one year from now (due to e.g. third-party apt repos changing their url, signing keys expiring, curl|bash installers that are no longer hosted, etc.). With Nix, every run will produce the same results, and sources for everything packaged in Nixpkgs are saved by nixos.org.
> One difference is that Docker containers use a separate file system isolated from the host, so you have to separately install your editor/shell in there, mount/clone your dotfiles, etc.
Can't you just mount a volume from your host machine? Then you can use your regular editor, and just run commands from inside the container.
While it is possible to work with containers to run programs with files on the host, it's just much more convenient to be able to run programs without dealing with mounting issues.
For an editor, maybe, for the files that you mount. But when e.g. a language server or IDE is involved, they need access to the tools that are now only inside the container. And then there's the shell itself, your shell history, etc.
On Linux absolutely, the IO latency on MacOS with mounted volumes is atrocious. Using a nix environment also avoids the weird edge cases I tend to run into with volumes mounts and file ownership and such.
It's not just about system time, it's about unpinned dependencies on external things. E.g. if you run apt update, after a package is updated upstream, it may install a newer version.
One thing is perfect caching. Each package is cached in its own folder, so if you exchange one package you don't have to rebuild the rest of the image.
Also you can have multiple versions of the package cached.
Also all your environments benefit from the cache, since each "layer" is independent.
Docker's layer based caching is very limiting for larger images. With Nix you spend basically no time on incremental builds outside of the time for the one package you changed.
You can also not only cache locally but cache nix builds online with services like Cachix. Then you can build a nix env on a powerful dev machine and push to the cache so when the Nix environment is built on a low powered CI machine it can easily download the build instead of trying to build and possibly time out.
Build once on one machine and all developers can just download the build.
Though it’s like comparing apples and oranges, the primary advantage over containers would be performance.
Nix (not to be confused with NixOS) is a package manager. Think of it like apt.
Containers on the other hand are (usually) utilizing kernel level isolation to run a whole user space starting with PID 1. These isolation techniques have overhead.
Since Nix is a user space application, you can run it in a container and Nix provides one `nixos/nix`.
Dockerfiles don't compose. If you have two containers and you want to take their union, you are simply out of luck unless you know exactly what you want to take from each one, and you'd better hope they don't interfere with each other. Nix specifications compose perfectly.
Nix is good at solving more problems than containers can solve. Nix is good at "programmatically managing packages".
For just the problem of "I want to be able to distribute the same application everywhere", container images solve this well. This can also be solved using Nix instead of container images; or Nix can even be used to build the container images.
The Nix expression language is much more expressive/powerful/elegant than Dockerfile's syntax is. -- Which can be useful for stuff like declaring "I want <program> built with different build flags / patches".
Nix ends up being useful for scratching the itch of "put in effort now, to save effort later".
The core advantage of nix as I understand it (and I would be happy to be corrected if I'm wrong):
- Build caching in containers treats the build process as a list of steps: first you do step 1, then step 2, etc. (these correspond to the *layers) of a container). If step 1 changes, you have to re-do step 2, even if it's unrelated; Containers don't have enough context about the commands they are running to know if step 2 depends on step 1.
- Build caching in nix is more like a directed acyclic graph: nix understands the steps that depend on other steps, and when something is changed, nix only has to re-run the steps that depended on that step, and the things that depend on them, etc.
Containers are way too slow, and take too much space (they don’t share dependencies). Also doesn’t necessarily play well with tools AND is hard to setup for dev environments (what do you mount?)
I don't use Apple products so I can't help you there. I assume it's doing some networking madness to expose a Linux VM through a bridge or something similar.
Nix has support for containers, and personally I think they are a beauty. Like the rest of the system.
The advantage of Nix is similar thought: The first green build of any revision is cached and kept as "that revisions build" forever. Similar like a container.
If I’m not mistaken, Nix uses cgroups as well on non-NixOS systems, so it is basically containers. You’re probably thinking about docker as a whole, in which Nix is effectively an alternative package manager/distribution system for containers.
Nix is basically a whole load of compiled dependencies pathed to /nix/hash/dependency
So you can have things that would ordinarily be dependency hell running side by side because foo that requires bar6 is compiled against that, and baz that requires bar7 is linked against that.
Both versions of bar are present in the nix structure, on a specific path that the software is compiled against.
Nix 'isolates' packages by ensuring that they do not know where to look for each other, rather than ensuring that they cannot possibly see each other.
In addition to the example given by the parent poster, here are some other steps taken towards that end in the Nix ecosystem:
Nix-built programs look for their libs, they don't see libs other than what they were built with because each package has its own little FHS-shaped tree, which it thinks of in the way a 'normal' package might think of as 'the system'— that thing which has a `/usr/lib` in which to find libraries and a `/etc` in which to find config files and a `/usr/share` in which to find assets, etc.
In addition to linking against hardcoded full paths to dependencies, outlined above, maintainers also take steps to ensure that, e.g., external programs referenced in shell scripts in a Nix package also refer to full paths into the Nix store.
How do you mean it doesn't if the manual itself says that: "In addition, on Linux, builds run in private PID, mount, network, IPC and UTS namespaces to isolate them from other processes in the system"?
Nix has configurable support for build sandboxing. On Linux, that sandboxing is enabled by default, but builds and installs and everything work fine without it.
Installing and using Nix packages doesn't generally involve any sandboxing or containerization features. But on Linux, there are some exceptions. A few proprietary packages use something called an FHSUserEnv, which leverages user namespaces to simulate an FHS-compatible environment. Additionally, Nix (through one of the new, experimental commands as well as an older third-party tool that inspired it) can also bundle any Nix package into a containerized package which can be run without Nix. I think those bundles, if you choose to create them, also use some container-y Linux features.
Anyway devenv.sh isn't built on anything container-y in Nix.
Overhead: Windows and macOS can't run Linux-based containers natively. Instead, there's always a full Linux virtual machine running in the background acting as an intermediary and host for your containers. Nix can conjure arbitrary native development environments on a per-command or per-terminal basis, giving you all the performance of directly running tools without the risk of clashing with systemwide software.
Reproducibility: Nix provides much stronger guarantees about the exact versions of software you're running. It effectively gives you a lockfile for your entire dependency chain, all the way down to libc. Containers tend to be more stateful: everyone on your team may be using the same Dockerfile, but if you build an image from it two weeks apart, you're probably going to get very different outputs due to things like your apt-get update step returning new versions of packages. This doesn't happen with Nix.
The beauty is that this isn't either/or; you can actually use Nix to generate OCI container images which are thus fully specified and repeatable.