Why use Nix package manager, even on macOS?

[#nix]

Some time ago I wrote a short article on nix first steps (https://dev.to/alexander_shagov/nix-first-steps-4066) . However, I realized that I need one more to quickly explain why I generally prefer nix over non-nix setups. So here it is.


So why bother with Nix when Homebrew already works?

Your project config is defined in a declarative style. Share it with a teammate and they get the exact same environment. Wipe your machine, run one command, and you’re back. No “works on my machine” excuses. With Home Manager the whole system config lives in code too. Version-controlled, easy to roll back, no version conflicts between projects. Want to try a newer global Node version without breaking anything? One line change, test, revert if needed. Someone might say, “Why not just use NVM?” Because NVM manages Node but not environments. It won’t guarantee your teammate has the same OpenSSL, pnpm, Python, libc bindings, or any other helper tools. Nix treats the whole setup as code so the system/project is reproducible instead of just “close enough”.

Each project also gets its own shell with its own tools. No global pollution. If a dependency update breaks something, pin it to the previous version in one edit and move on.


This blog you’re reading right now is a concrete example. It’s built with Hugo, and the only thing I need to run it locally is a flake.nix sitting in the repo root:

{
  inputs = {
    nixpkgs.url = "nixpkgs";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          config.allowUnfree = true;
        };
      in {
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            hugo
            direnv
          ];

          shellHook = ''
            eval "$(direnv hook bash)"
            echo "Using Hugo: $(hugo version)"
          '';
        };
      });
}

That’s it. I clone the repo, run nix develop, and I have Hugo in an isolated shell. No global install, no version mismatch between my laptop and desktop, no “which hugo was this using again?” The environment is the file.


I held off for a while. Homebrew + rbenv + nvm + Docker worked fine. But after using Nix across two machines and multiple projects, the consistency sold me. Same shell, same tools, same versions everywhere.

The syntax is weird at first and the error messages aren’t great. Still worth it if you switch machines or juggle multiple projects.

If you’re just starting out, check the First Steps article to get the vocabulary straight first.