LabCraft course question: should beginner Nix material teach classic or modern CLI first?

Happy Friday everyone!

For those that are unaware, I’m building LabCraft, a hands-on learning platform for infrastructure and systems engineers. I talked about this in a post I made this earlier this week.

Nix is the first course because it is the learning problem in miniature: package management, shells, build systems, OS configuration, and a lot of ecosystem history all overlap.

The opening part is therefore aimed at people who do not know Nix yet. It runs in a non-NixOS environment (Ubuntu), and learners install Nix as a package manager, inspect /nix/store, look at the daemon/build-user/profile model, try ephemeral shells, search nixpkgs, write shell-scripts with shebangs that declare their dependencies, and uninstall cleanly.

The design question I’m unsure about is the command surface.

Should the material be classic-first?

nix-shell -p jq
nix-env -qaP ripgrep
nix-store --query --requisites ...


#!/usr/bin/env nix-shell
#!nix-shell -i bash -p curl jq

The case for this is strong: a lot of existing docs, answers, scripts, and institutional memory use these commands. Beginners will see them in the wild.

Or should it be modern-first?

nix shell nixpkgs#jq --command jq --version
nix search nixpkgs ripgrep
nix path-info --recursive ...


#!/usr/bin/env nix
#! nix shell nixpkgs#bash nixpkgs#curl nixpkgs#jq --command bash

The case for this is also strong: it lines up better with flakes, nix develop, devShells, and the direction many newer projects use and teach.

My current leaning is:

  • Teach the modern CLI as the main path.

  • Show classic equivalents nearby as “you will see this in older docs.”

  • Teach translation explicitly rather than pretending the ecosystem has one settled surface.

  • Avoid making flakes the whole topic too early, but not hide the fact that modern commands and flakes are connected.

I figured the best solution would be to seek community input on this. For people who teach, document, mentor, or onboard new Nix users:

  1. Would you prefer beginner material to be classic-first or modern-first?

  2. Is it more harmful to introduce nix-command flakes early, or to start with commands like nix-shell / nix-env?

  3. Are there commands you think beginner material should avoid entirely?

  4. What classic ↔ modern translation table should every new Nix user learn?

  5. Where do new users actually get stuck in practice?

I’m not looking for a purity answer as much as an onboarding answer. The goal is to help a new user build judgment and still be able to read the real-world Nix material they will encounter afterward.

Thanks

1 Like

i would teach the thing that the project supports without opting into features marked as experimental with an uncertain future. Having a base grounded in how nix works, how nixpkgs is just a massive attr set, how imports work, how fetching works, how to pin with and without a tool. All of that can be learnt without touching flakes, and if in the end you want to land on flakes, it then makes much more sense in terms of what flakes are trying to achieve, given the foundation they sit on.

I’ve found tons of success just by getting people to open the repl, evaluate some dumb expressions to get a feel for how nix calculates stuff, then trivial-builders to make some silly shell scripts, which neatly shows “${getExe pkgs.curl}” doing its auto dependency magic, then fods, then packaging a simple application by pulling the source as a FOD and invoking any build system. Then a bit of stdenv, point them at the nixpkgs manual and let them rip (caveat: all mid to senior software engineers). None of that needs flakes, or nix3 commands etc, but if you have all of that in hand, then flakes aren’t magic, they are just a somewhat standardised packaging format for everything you just learnt. Take it or leave it. The internet (reddit in particular) is absolutely full of people who think flakes are pre-req for consuming any remote nix code and are really woolly on the fundamentals because they jumped right to the end.

8 Likes

Yeah, it’s way easier to tell someone “now take all of that nix knowledge and produce an attrset in this shape to set defaults for your nix commands” than “unlearn everything you’ve learned so far, this is what an attrset is”.

It’s really unfortunate the nix3 CLI is so married to flakes, it would be way more “intuitive” to someone familiar with modern CLI design, and you have to caveat nix-env with “basically don’t use this, but you need to use it”.

I don’t think a good way to teach nix exists at the moment, simply because of the half-baked implementation of the new stuff and all the historic footguns in the old stuff. Really can’t wait for lix to finish the unflakening of the nix3 CLI.

It depends on what your goal is, but nix-env -i and nix profile are very niche and should largely only be used by deployment tools like NixOS and home-manager.

It’s much easier to teach someone how to use their deployment tool from within a working deployment tool than to teach them how to build their own.

Be ready to have to at least discuss this, though. Some users resent this, and will want to learn “pure” nix, which leads to them trying to learn a lot of confusing details around nix-env -i and nix profile, rather than understanding the language or how the ecosystem works.

It’s best to try and deflect such users to nix-shell or nix shell, but that can be difficult with the class of users who think of nix as a package manager first and foremost.

IME the jump from nix expressions to the module system is the biggest hurdle.

Most newcomers are at best halfway through understanding nix syntax when they try out NixOS, and then they get to be confused by the self-referential, fixed-point module eval.

I’ve not really found a good way to teach this yet; it’s an inherently paradoxical and confusing thing, getting intuition for how you use it to write “good” code is hard, and it’s very functional-programming flavored, so many people will have to bootstrap on that, too.

It’s even worse if you want to bootstrap them on flake-parts, since its module args and lib are horrifyingly confusing even if you understand it.

Another issue tends to be figuring out how import works, and how it relates to callPackage and imports (in the module system).

I don’t think this is all that important, users should figure out how to use their commands as they use them.

To someone who learned how to use the classic CLI, understanding the split between nix shell and nix develop can be useful, everything else a real user will touch is named identically anyway.

5 Likes

I don’t see why you can’t use nix3 without flakes.

3 Likes

I think showcasing that this is possible would also be very helpful to users. Many users I’ve spoken to don’t realize that this is actually possible and implicitly assume nix-command = flakes

1 Like

Have you considered simply pointing your users to the relevant parts of what currently is https://nix.dev and pick them up when they are done? Basic concepts and workflows are outlined in the beginner tutorial series. The most important aspects are the Nix language, the various Nixpkgs mechanisms for constructing derivations to build software, and the module system.

You could even support the documentation team with improving the material to the point where it puts your audience in a place where you need them to continue teaching them what you actually wanted to teach them.

You may correctly object that this would be quite the detour. I worked on improving Nix documentation as my day job for a couple of years: there’s no royal road to Nix, as all the “it’s complicated” answers indicate. For more specific discussion of flakes or not, see: Discussion of flakes, from 2025 NixOS Community Survey Report

4 Likes

Something I missed in the first pass: Don’t talk about profiles via nix-env or nix3 equivs. Nix is a terrible package manager with terrible package management semantics. My slogan for explaining nix is “make for the whole system”. I think this covers the way good nix usage looks like much better than a package manager analogy. nix-env doesn’t even need to be mentioned aside from “ignore it”. I would talk about garbage collection, I would talk about how to prevent nix-collect-garbage from cleaning up your work, because those are useful pragmatic topics and doing them well will equip people with what they need for success and cover profile mechanics without naming sub commands that they have no reason to invoke.

3 Likes

E.g. nix develop inherently relies on flakes. I’m sure many things can be done, though, you’re right.

An update for everyone who contributed here: Nix First Contact has shipped with the modern CLI as its main path and classic equivalents shown where learners are likely to encounter them in older documentation and existing projects.

LabCraft is also open in public beta now. I posted the announcement and feedback request in a new topic.

If you try the course, I would especially value feedback on how the classic and modern command surfaces are introduced.