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.
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.
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.
This works going backwards, too. If you need an older version of some software, Nix can happily install it for you in a way that won't conflict with other software on your system. The only requirement is that you can still get the original sources and you can get a copy of the nixpkgs repo at the time the older version of that software was initially committed.
> If apt could compile all its packages from source with an option to do so and a cache, it would be as reproducible.
This is simply wrong. There are many source-based package managers besides Nix: Portage, Sorcery, Pkgsrc, Ravenports, MacPorts, FreeBSD Ports, etc. Some of them, like Homebrew for example, even support binary caching. None of them have the same kind of reproducibility guarantees as Nix offers, except for Guix.
The counterfactual about apt doesn't even really make sense, as using its own special source archive format, having binary repos, and performing install time dependency resolution based on version constraints are all pretty essential to what makes it apt. But even so, the mere fact that it installs to global, shared locations, e.g., under `/usr`, means that there are circumstances where you can't guarantee that a package will always load the same libraries it was built against, namely in the case of transitive dependencies which had different versions at build time.
Guix, a similar project to Nix, has been tackling this head on by automatically falling back to the Software Heritage archive when the original upstream is unavailable: https://guix.gnu.org/en/blog/2019/connecting-reproducible-de...
i.e. if a package depends on the systemd package https://search.nixos.org/packages?channel=unstable&show=syst... , Nix will not automatically find a replacement to run the package on Mac. But it may be possible to manually work around this with https://github.com/LnL7/nix-darwin
More on building Docker images with Nix: https://nix.dev/tutorials/building-and-running-docker-images
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.
> i.e. if a package depends on the systemd package https://search.nixos.org/packages?channel=unstable&show=syst... , Nix will not automatically find a replacement to run the package on Mac. But it may be possible to manually work around this with https://github.com/LnL7/nix-darwin
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
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.
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.
Any way to hack this? Just pretend it's a different date, the pointers to the latest package will changed to reflect that.
Or somehow record the exact versions when you first build the container, and freeze that.
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.
Build once on one machine and all developers can just download the build.
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`.
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".
- 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.
In what way are they slow?
- Nix is very good at cross platform support. A single entry-point creates environments on both MacOS and Linux.
- Docker containers run slower on MacOS because the virtualization overhead.
- Docker images can provide a reproducible environment but the images themselves aren’t reproducible.
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.
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.
https://nixos.org/manual/nix/stable/command-ref/conf-file.ht...
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.