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 can get the same declared environment; with a committed flake.lock, they also get the same pinned inputs. Wipe your machine, run one command, and you’re back. No “works on my machine” excuses. With Home Manager , your user environment lives in code too; if you use NixOS or nix-darwin, system configuration can live there as well. 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 does not manage or pin the broader system and native dependency graph, OpenSSL, Python, 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 (and lockfile) sitting in the repo root:

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

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

          shellHook = ''
            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 flake plus the lock 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.