8 ms·
I have been exploring nix for the past few months and my experience with nix has been both exhilarating and frustrating, simultaneously. On one hand, I find it
by amardeep 4y ago
I have been exploring nix for the past few months and my experience with nix has been both exhilarating and frustrating, simultaneously. On one hand, I find it hard to imagine not using nix now, but on the other hand, I hesitate to recommend it to other colleagues due to its steep learning curve, ux issues and potential for footguns.
I sincerely hope that nix community improves the UX to make it more accessible to new users. Though for those willing to invest time in learning it, nix is extremely useful and highly recommended.
- domenkozar 4y agoHey! We've been working at Cachix to address this using https://devenv.sh/ https://devenv.sh/. It's primary targeted to improve DX and on-board users quickly.
- amardeep 4y agoThanks Domen, devenv.sh is quite nice, and have already started using it.
- domenkozar 4y agoDo you still experience these UX issues and lack of documentation?
- pbronez 4y agoDevEnv looks neat, maybe I’ll try it. I expect my main hurdle will be stuff that isn’t in NicPkgs. Especially for dev stuff, I’m going to want to pull in low-profile GitHub stuff or Python packages. What’s the escape hatch to bring those into the dev environment?
- rgoulter 4y ago> What’s the escape hatch to bring those into the dev environment? One thing you can do is try the Nix package manager on your Linux or macOS system. -- If it's not in nix, you can just do it the way you did things before. If you want to try NixOS: 1. Writing a package is only necessary for sharing code.. you can still just have Python, setup a virtual env, & run the program as you would. 2. You could use a tool like Podman or Docker, to run stuff in containers. I've heard stuff like https://github.com/containers/toolbox https://github.com/containers/toolbox helps this. (Among other solutions).
- chriswarbo 4y ago> What’s the escape hatch to bring those into the dev environment? The function `pkgs.runCommand` is useful if you just want to run some Bash commands https://nixos.org/manual/nixpkgs/stable/#trivial-builder-runCommand https://nixos.org/manual/nixpkgs/stable/#trivial-builder-run... The main difference compared to running commands in a normal terminal is that builds are sandboxed, with no network access by default: - If your commands need to download some particular files, you can have Nix fetch them separately (e.g. using `fetchurl`, `fetchGit`, etc.) and provide them to your commands via env vars. See https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers - If you don't know what will be downloaded, or there's no way to run in an 'offline' mode, then you can specify hash for the result (making it a "fixed output derivation"). That will give it network access, and Nix will check that the output matches the given hash for reproducibility (you can just make up a random hash to start with; Nix will reject the result, telling you its hash, which you can copy/paste into the definition :) ) The reasons I like this approach include: - Bash is familiar/traditional and mostly-compatible with e.g. official install instructions provided by many projects, Stack Overflow answers, blog posts and tutorials, etc. - Powerful/unrestricted, in case we need to do some fiddling between some steps - Nix often reveals problems with those familiar/traditional instructions; e.g. if some deeply-nested part of an installer happens to run Python, it will fail if Python wasn't explicitly listed in its dependencies (AKA `buildInputs`). Revealing and fixing such things up-front avoids the "works on my machine" problem. - Bash commands are often tedious and inflexible; so after writing a few of these we may find ourselves wanting more structure, more reusable parts, etc. which is exactly what the helper functions in Nixpkgs provide (like `pkgs.stdenv.mkDerivation`, `pkgs.pythonPackages.buildPythonApplication`, etc.). In contrastt, starting off with those helper functions can seem overwhelming, and the benefits may be hard to appreciate immediately.
- amardeep 4y agoFor python, you could just install bare python with venv and/or poetry and manage your dev python dependencies outside nix. For eg. if you were using devenv.sh, there is an example here: https://github.com/cachix/devenv/tree/main/examples/python-poetry https://github.com/cachix/devenv/tree/main/examples/python-p...
- tripdout 4y agoThis looks like a really nice UX improvement on top of typical Flake devshells.
- theptip 4y agoI think this project is really promising. My main concern is that it puts another layer of abstraction atop an already complex (and at times leaky) abstraction. I’d love to see more clear docs about what devenv is actually doing under the covers, and how to escape-hatch into Nix land when I inevitably need to tweak something. Also, similarly, how do I map Nix docs (often just a set of example expressions) into equivalent devenv incantations? (It’s been a few months since I last looked so maybe things have come along since then.)
- __MatrixMan__ 4y agoI think that's a valid concern. You read the nix pills and think you know what you're doing and then it turns out that the community has wrapped the things you've learned about in things you've never heard of, so you still can't learn from other people's repos.
- sbt567 4y agoThis. I've been actively trying nix-based tooling on and off for my projects because it is legitimately solve the problem around sandboxing, versioning, reproducibility, consistency, etc. Recently, I'm trying to use asdf (and its faster alternative, rtx) and I keep telling myself "huh, nix could solve this problem better". But, damn it is infuriating to learn. Like other commenter said, it is the escape hatch that I'm missing so much. I really really want to convince myself to learn nix The Right Way. But, it feels like you will have another learning curve when using a nix-wrapper tooling. I still have a high hope for the future of nix. And I believe the time nix will rise in popularity is when they have sorted the UX-related issues.
- __MatrixMan__ 4y agoOne thing that has helped somewhat is being more thorough in my exploration of repo's that are doing things that are similar to what I'm trying to do. I want to use nix with a nim project, so I wrote a script that walks through all of the repo's in nimble (nim's package manager) and then filtered those for ones that had a flake.nix in the repo root. Going through them was a helpful dose of context. It's like you gotta approach it from theory to practice and from practice to theory and eventually your efforts will meet in the middle. I think. I have glimmers of meeting in the middle happening.
- pveierland 4y agoCurrently working on a graphical UX where you can create and share flakes without writing Nix code at https://mynixos.com https://mynixos.com The site makes it easy to browse indexed flakes and configure flakes via options and packages. Hopefully the structure provided by the UI can makes it easier to get started with Nix flakes :)
- pbronez 4y agoVery cool!
- tomberek 4y agoThere are two sides to this problem, the first is to improve the UX, but the second is to clearly describe a compelling reason for people to adopt. It is very tempting to only blame the first, but I think we need to also need to tell a better story and highlight the values in a better way. This would then give people a reason to get past the UX issues in the hopes of achieving those desired values. For example; people seem to have accepted that the benefits of using terraform in spite of various difficulties - the learning of its language or needing to hire specialists. The mantra of "infrastructure as code" is enough to drive adoption. What is our mantra? We need to accept that "reproducibility" isn't quite working and that we need either a clearer message, or to explain the message.
- carapace 4y agoI disagree, I think the value proposition for reproducibility is clear, it's just that the learning curve "is too damn high!" I'm highly motivated to learn and use Nix (or Guix for that matter) but I've bounced off of it three or four times now, and I'm the kind of weirdo who learns new PLs for fun. Someone once said that you don't learn Nix, you reverse engineer it.
- takeda 4y agoI think the reason why Nix is hard isn't the language (although lazily evaluated functional language is definitively a part of it), but the paradigm shift, but this is necessary for reproducibility. Imagine if you had a distro where you couldn't depend on existing state (so you couldn't just compile something to /usr/local). Where you had to create package definition with all dependencies explicitly defined. Kind of like using FreeBSD ports (or Gentoo) without precompiled packages and you were forced to add every package manually before using. You would also complain it is hard even if you would have to just use Makefile and shell script. I don't think this will get easier until Nix would get embraced by other package s, making it easy to specify those components as dependencies. Now with flakes with can be composable this is possible.
- IshKebab 4y agoI agree with you. I can see the value of reproducibility, declarative system setup, etc. I would love that. But there's no way I'm fighting software with such a bad UX.
- lostmsu 4y agoWhat are the footguns? I feel that footgun descriptions always have the most insightful bits about technology.
- chriswarbo 4y agoA few antipatterns/annoyances I've come across over the years: Importing paths based on environment variables: There is built-in support for this, e.g. setting the env var `NIX_PATH` to `a=/foo:b=/bar`, then the Nix expressions `<a>` and `<b>` will evaluate to the paths `/foo` and `/bar`, respectively. By default, the Nix installer sets `NIX_PATH` to contain a copy of the Nixpkgs repo, so expressions can do `import <nixpkgs>` to access definitions from Nixpkgs. The reason this is bad is that env vars vary between machines, and over time, so we don't actually know what will be imported. These days I completely avoid this by explicitly un-setting the `NIX_PATH` env var. I only reference relative paths within a project, or else reference other projects via explicit git revisions (e.g. I import Nixpkgs by pointing the `fetchTarball` function at a github archive URL) Channels: These always confused me. They're used to update the copy of Nixpkgs that the default `NIX_PATH` points to, and can also be used to manage other "updatable" things. It's all very imperative, so I don't bother (I just alter the specific git revision I'm fetching, e.g. https://hackage.haskell.org/package/update-nix-fetchgit https://hackage.haskell.org/package/update-nix-fetchgit helps to automate such updating). Nixpkgs depends on $HOME: The top-level API exposed by the Nixpkgs repository is a function, which can be called with various arguments to set/override things; e.g. when I'm on macOS, it will default to providing macOS packages; I can override that by calling it with `system = "x86_64-linux"`. All well and good. The problem is that some of its default values will check for files like ~/.nixpkgs/config.nix, ~/.config/nixpkgs/overlays.nix, etc. This causes the same sort of "works on my machine" headaches that Nix was meant to solve. See https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/impure.nix https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/... I avoid this by importing Nixpkgs via a wrapper, which defaults to calling Nixpkgs with empty values to avoid its impure defaults; but still allows me to pass along my own explicit overrides if needed. The imperative nix-env command: Nix provides a command called 'nix-env' which manages a symlink called ~/.nix/profile. We can run commands to "install packages", "update packages", "remove packages", etc. which work by building different "profiles" (Nix store paths containing symlinks to a bunch of other Nix store paths). This is bad, since it's imperative and hard to reproduce (e.g. depending on what channels were pointing to when those commands were run, etc.). A much better approach is to write down such a "profile" explicitly, in a git-controlled text file, e.g. using the `pkgs.buildEnv` function; then use nix-env to just manage that single 'meta-package'. Tools which treat Nix like Apt/Yum/etc. This isn't something I haven't personally done, but I've seen it happen in a few tools that try to integrate with Nix, and it just cripples their usefulness. Package managers like Apt have a global database, which maps manually-written "names" to a bunch of metadata (versions, installed or not, names of dependencies, names of conflicting packages, etc.). In that world names are unique and global: if two packages have the name "foo", they are the same package; clashes must be resolved by inventing new names. Such names are also fetchable/realisable: we just plug the name and "version number" (another manually-written name) into a certain pattern, and do a HTTP GET on one of our mirrors. In Nix, all the above features apply to "store paths", which are not manually written: they contain hashes, like /nix/store/wbkgl57gvwm1qbfjx0ah6kgs4fzz571x-python3-3.9.6, which can be verified against their contents and/or build script (AKA 'derivation'). Store paths are not designed to be managed manually. Instead, the Nix language gives us a rich, composable way to describe the desired file/directory; and those descriptions are evaluated to find their associated store paths. Nixpkgs provides an attribute set (AKA JSON object) containing tens of thousands of derivations; and often the thing we want can be described as 'the "foo" attribute of Nixpkgs', e.g. '(import <nixpkgs> {}).foo' Some tooling that builds-on/interacts-with Nix has unfortunately limited itself to only such descriptions; e.g. accepting a list of strings, and looking each one up in the system's default Nixpkgs attribute set (this misunderstanding may come from using the 'nix-env' tool, like 'nix-env -iA firefox'; but nix-env also allows arbitrary Nix expressions too!). That's incredibly limiting, since (a) it doesn't let us dig into the structure inside those attributes (e.g. 'nixpkgs.python3Packages.pylint'); (b) it doesn't let us use the override functions that Nixpkgs provides (e.g. 'nixpkgs.maven.override { jre = nixpkgs.jdk11_headless; }'); (c) it doesn't let us specify anything outside of the 'import <nixpkgs> {}' set (e.g. in my case, I want to avoid NIX_PATH and <nixpkgs> altogether!) Referencing non-store paths: The Nix language treats paths and strings in different ways: strings are always passed around verbatim, but certain operations will replace paths by a 'snapshot' copied into the Nix store. For example, say we had this file saved to /home/chriswarbo/default.nix: # Define some constants with { # Import some particular revision of Nixpkgs nixpkgs = import (fetchTarball {...}) {}; # A path value, pointing to /home/chriswarbo/defs.sh defs = ./defs.sh; # A path value, pointing to /home/chriswarbo/cmd.sh cmd = ./cmd.sh; }; # Return a derivation which builds a text file nixpkgs.writeScript "my-super-duper-script" '' #!${nixpkgs.bash}/bin/bash source ${nixpkgs.lib.escapeShellArg defs} ${cmd} foo bar baz '' Notice that the resulting script has three values spliced into it via ${...}: - The script interpreter `nixpkgs.bash`. This is a Nix derivation, so its "output path" will be spliced into the script (e.g. /nix/store/gpbk3inlgs24a7hsgap395yvfb4l37wf-bash-5.1-p16 ). This is fine. - The path `cmd`. Nix spots that we're splicing a path, so it copies that file into the Nix store, and that store path will be spliced into the script (e.g. /nix/store/2h3airm07gp55rn9qlax4ak35s94rpim-cmd.sh ). This is fine. - The string `nixpkgs.lib.escapeShellArg defs`, which evaluates to the string `'/home/chriswarbo/defs.sh'`, and that will be spliced into the script. That's bad, since the result contains a reference to my home folder! The reason this happens is that paths can often be used as strings, getting implicitly converted. In this case, the function `nixpkgs.lib.escapeShellArg` transforms strings (see https://nixos.org/manual/nixpkgs/stable/#function-library-lib.strings.escapeShellArg https://nixos.org/manual/nixpkgs/stable/#function-library-li... ), so: - The path `./defs.sh` is implicitly converted to the string `/home/chriswarbo/defs.sh`, for input to `nixpkgs.lib.escapeShellArg` (NOTE: you can use the function `builtins.toString` to do the same thing explicitly) - The function `nixpkgs.lib.escapeShellArg` returns the same string, but wrapped in apostrophes (it also adds escaping with backslashes, but our path doesn't need any) - That return value is spliced as-is into the resulting script To avoid this, we should instead splice the path into a string before escaping; giving us nested splices like this: source ${nixpkgs.lib.escapeShellArg "${defs}"}
- f0e4c2f7 4y agoI find there is a perfect middle ground here (applies to multiple languages like this.) Nix + LLM. Especially if using GPT4 for quality. You get all of the usefulness of Nix, but created or edited using plain English. And then of course at the end you output the nix as an artifact to live in version control.
- drakerossman 4y agoWhat I see as a barrier for entry to Nix/NixOS is not the UX, but the available documentation, or lack of thereof. One may consider the docs being part of UX though. I am in the process of writing a book about NixOS, you may track the progress here: https://drakerossman.com/blog/practical-nixos-the-book https://drakerossman.com/blog/practical-nixos-the-book The emphasis is on how to make it as practical as possible, plus cover the topics which may apply to Linux in general, and in great detail.
- tacon 4y agoYour email form to follow the book is returning errors to me. And you have no other contact channels I can find. Might want to look into that.
- drakerossman 4y agoThank you for pointing that out! I have a twitter with the same handle, if you would like to subscribe to that. If not - wouldn't you mind a follow-up email when I fix the subscription form?
- ronef 4y agoExcited to see this being in progress. Have you checked out the docs effort spun up with the Nix Documentation team?
- drakerossman 4y agoYes, and was, to be quite honest, not quite impressed. They lay a foundation stone, but don't actually go that much further.
- bsder 4y agoThe problem with Nix is that I still have to start with a Linux system--so I still need Docker, Terraform, something to give me a stable base for Nix to work against. At that point--why should I add Nix to the mess since I still need those other things anyway?
- biggestlou 4y agoThis is not true. Nix is supported on several non-Linux platforms, including macOS and Windows WSL.
- ptman 4y agoWSL2 is very much Linux. Microsoft got tired of trying to reimplement Linux kernel interfaces so they just ship the Linux kernel in a VM.
- soraminazuki 4y agoFor what reason do you "still need Docker?" With Linux, the only stable base required for Nix to function is the kernel. Nix packages all the required dependencies right down to glibc. Since the Linux kernel famously "doesn't break userspace," any sufficiently new kernel would suffice. Until recently, I've been able to get the latest Nix packages working on an ancient Linux 2.6 kernel. And even the kernel can be managed with Nix if you use NixOS. But Docker can't, so it's no use here. As for Terraform, I don't see how it's relevant to this discussion. Nix-based SSH deployment tools can replace some of its functionality, so perhaps that's what you're talking about?
- MuffinFlavored 4y ago> On one hand, I find it hard to imagine not using nix now for what specifically?