TL;DR

The author migrated a macOS development environment from imperative Homebrew workflows to a declarative, immutable setup using Nix and nix-darwin. Benefits include reproducibility, rollbacks and ephemeral shells; trade-offs are a steep learning curve and friction with some GUI apps.

What happened

The author spent months moving a complete macOS workflow into Nix and nix-darwin to avoid the "imperative rot" that comes from ad-hoc installs (curl | sh, brew install, etc.). Nix treats a system as a pure function of its configuration: packages are stored under /nix/store with unique hashes, multiple versions can coexist, and previous system generations can be booted or listed with nix-env. Flakes (flake.nix and flake.lock) pin exact dependency commits so the same configuration builds identically on multiple machines. The writer uses darwin-rebuild switch –flake . to apply changes, and relies on nix shell for ephemeral, non-persistent development shells. Because some macOS GUI apps expect /Applications and self-updates, the author adopts a hybrid approach: manage core tooling and settings with nix-darwin while delegating big GUI apps to Homebrew via a Nix homebrew module. The post ends with practical starting steps and links to configuration examples.

Why it matters

  • Reproducibility: flake.lock pins exact commits so builds are repeatable across machines.
  • Safety: previous system generations remain available for rollbacks if an update breaks the setup.
  • Cleaner temporary workflows: nix shell provides ephemeral environments that leave no global state behind.
  • Declarative system settings: nix-darwin can configure macOS preferences (Finder, Dock, trackpad) as code.
  • Practical trade-offs: significant upfront learning and occasional macOS GUI compatibility issues.

Key facts

  • Nix stores packages in /nix/store with unique hashes, allowing side-by-side versions.
  • System generations can be listed and rolled back; example command shown is sudo nix-env –list-generations -p /nix/var/nix/profiles/system.
  • Nix flakes use a flake.nix (blueprint) and flake.lock (time-capsule) to pin dependency commits.
  • darwin-rebuild switch –flake . applies a flake-based configuration on macOS with nix-darwin.
  • nix shell <pkg> creates an ephemeral shell with that package available; exiting removes the environment.
  • nix shell is not equivalent to a container or VM—it's not fully sandboxed and runs on the host kernel.
  • Some GUI macOS apps expect to live in /Applications and self-update; these can conflict with the read-only Nix store.
  • A hybrid model can be used: manage core tools and settings with nix-darwin and use Homebrew (via a Nix module) for certain casks.
  • Author recommends using the Determinate Nix Installer for macOS multi-user setup and committing flake.lock as source of truth.

What to watch next

  • The steep learning curve: expect time investment to understand the Nix language, links, and interactions with macOS volumes.
  • Compatibility of GUI apps that self-update or expect /Applications; monitor which apps require Homebrew or manual handling.
  • Maintenance of flake.lock: keep it versioned and committed to ensure reproducible builds across machines.

Quick glossary

  • Nix: A package manager and build system that treats packages and system configuration as pure functions, enabling reproducibility.
  • nix-darwin: A set of tools and modules that apply Nix-based configuration management to macOS system and user settings.
  • Flake: A reproducible Nix project format containing a flake.nix (declaration) and flake.lock (pinned dependency commits).
  • Nix store: The directory (typically /nix/store) where Nix places built packages, each with a unique hash-based path.
  • Ephemeral shell (nix shell): A temporary environment that exposes specified packages without installing them globally; the environment is removed when you exit.

Reader FAQ

Can nix-darwin configure macOS preferences like Dock or Trackpad?
Yes. nix-darwin can express many Finder, Dock and system preferences in configuration files.

Will GUI apps installed via Nix appear in /Applications and update themselves?
Not reliably. Many GUI apps expect to live in /Applications and self-update; the author recommends using Homebrew casks (via a Nix module) for those.

Is nix shell equivalent to running a container or VM?
No. nix shell provides an isolated environment for variables and paths but runs on the host kernel and is not a sandboxed container.

How do I begin migrating my macOS setup to Nix?
Start by using the Determinate Nix Installer, initialize a flake and home-manager, modularize configuration files, and commit your flake.lock.

Going immutable on macOS by  Antonin December 31, 2025   ·  8 min read Table of contents The problem to solve: imperative rot System immutablility, and the Nix store The…

Sources

Related posts

By

Leave a Reply

Your email address will not be published. Required fields are marked *