Reproducible macOS Configurations with Nix

Reproducible macOS Configurations with Nix

July 9, 2024
The Nix logo

What is Nix?

Nix is a package manager 1 for Linux and macOS. Together with nix-darwin, it lets you describe much of your macOS setup in a configuration file. A flake can pin dependencies, making that setup easier to reproduce on another machine.

A key component of the Nix ecosystem is Nixpkgs, the default repository of Nix packages, which contains thousands of searchable packages that you can easily integrate into your configuration.

Why Use Nix on macOS?

If you use macOS, you may already know Homebrew as a package manager. Nix also manages packages; with nix-darwin, it can manage system settings as well.

It offers many advanced features, such as binary caching, package pinning, package overrides, automatic runtime dependencies, and custom package sources.

One particularly interesting part of the Nix ecosystem for macOS users is nix-darwin, a set of tools and configurations that allows you to manage your macOS system configuration using Nix. This is what we will go over in this post.

This walkthrough began in mid-2024. The nix-darwin template, repository URL, and switch commands below were refreshed in September 2026, but the full package list and configuration have not been retested on a new Mac. Check the current nix-darwin README before applying a system configuration.

Installing Nix

There are multiple ways to install Nix on your macOS, but I found that the most reliable way is to use the DeterminateSystems/nix-installer script by running 2

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

Setting up nix-darwin

Once you have Nix installed, you can set up nix-darwin by running the following command:

# Create and navigate to a new directory for your nix-darwin configuration
mkdir -p ~/.config/nix-darwin-config
cd ~/.config/nix-darwin-config

# initialize a new nix flake with the nix-darwin template
nix flake init -t nix-darwin/master

This last command will create a flake.nix file in your nix-darwin-config directory. This file is where you will define your system configuration. You will want to change the following configuration options:

  • nixpkgs.hostPlatform to aarch64-darwin (for Apple Silicon) or x86_64-darwin (for Intel Macs).
  • nixpkgs.config.allowUnfree to true if you want to allow unfree packages (e.g. steam).

you can also add any system package you need in the environment.systemPackages array, for example:

environment.systemPackages =
    [ pkgs.vim
        pkgs.pet
        pkgs.lazygit
        pkgs.lazydocker
        pkgs.fzf
    ];

This is a minimal configuration to get you started. You can add more packages and customizations as needed. A full list of available options can be found in the nix-darwin manual.

I like to keep my configuration in a git repository, along with my dotfiles, so I can easily share it across machines.

Your configuration file will probably look something like this:

{
  description = "Victor's MacOS Nix System flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    nix-darwin.url = "github:nix-darwin/nix-darwin/master";
    nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs@{ self, nix-darwin, nixpkgs }:
  let
    configuration = { pkgs, ... }: {
      # List packages installed in system profile. To search by name, run:
      # $ nix-env -qaP | grep wget
      environment.systemPackages =
        [ pkgs.vim
        pkgs.fzf # Fuzzy finder
        pkgs.pet # Snippet manager
        pkgs.lazydocker # Docker TUI
        pkgs.lazygit # Git TUI
        pkgs.ripgrep # Faster grep
        pkgs.zoxide # Directory jump tool (z)
        pkgs.pyenv # Python version manager
        pkgs.gh # GitHub CLI
        pkgs.go # Go programming language
        pkgs.kubectl # Kubernetes CLI
        pkgs.kubectx # Kubernetes context switcher
        pkgs.tenv # OpenTofu, Terraform, Terragrunt and Atmos version manager
        pkgs.delta # Terminal git diff viewer with syntax highlighting
        pkgs.curl # Command line tool for transferring data with URL syntax
        pkgs.jq # Command line JSON processor
        pkgs.yq # Command line YAML processor
        pkgs.just # Command runner, similar to make
        pkgs.vscode # Visual Studio Code
        pkgs.neovim # Vim-fork focused on extensibility and usability
        pkgs.helix # Vim-like modal text editor
        pkgs.k9s # Kubernetes CLI to manage your clusters in styles
        pkgs.pandoc # Universal document converter
        pkgs.python3 # Python 3 programming language
        pkgs.rustc # Rust programming language
        pkgs.rustup # Rust toolchain installer
        pkgs.lorri # Nix shell manager
        pkgs.htop # Interactive process viewer
        pkgs.tree # Display directories as trees
        pkgs.jetbrains-mono # JetBrains Mono font
        pkgs.ffmpeg # Multimedia framework
        pkgs.alacritty # GPU-accelerated terminal emulator
      ];

      # allowUnfree is required to install some packages that are not "free" software.
      nixpkgs.config.allowUnfree = true;

      # Necessary for using flakes on this system.
      nix.settings.experimental-features = "nix-command flakes";

      # Create /etc/zshrc that loads the nix-darwin environment.
      programs.zsh.enable = true;  # default shell on catalina
      # programs.fish.enable = true;

      # Set Git commit hash for darwin-version.
      system.configurationRevision = self.rev or self.dirtyRev or null;

      # Used for backwards compatibility, please read the changelog before changing.
      # $ darwin-rebuild changelog
      system.stateVersion = 4;

      system.defaults = {
        dock.autohide = true;
        dock.mru-spaces = false; # Most Recently Used spaces.
        finder.AppleShowAllExtensions = true;
        finder.FXPreferredViewStyle = "icnv"; # icon view. Other options are: Nlsv (list), clmv (column), Flwv (cover flow)
        screencapture.location = "~/Pictures/screenshots";
        screensaver.askForPasswordDelay = 10; # in seconds
      };

      # The platform the configuration will be used on.
      nixpkgs.hostPlatform = "aarch64-darwin";

      nix.extraOptions = ''
        extra-platforms = x86_64-darwin aarch64-darwin
      '';
    };
  in
  {
    # Build darwin flake using:
    # $ darwin-rebuild build --flake .#simple
    darwinConfigurations."simple" = nix-darwin.lib.darwinSystem {
      modules = [ configuration ];
    };

    # Expose the package set, including overlays, for convenience.
    darwinPackages = self.darwinConfigurations."simple".pkgs;
  };
}

Applying the Configuration

To apply your configuration, run the following command:

sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake .#simple

To update your configuration, you can run:

nix flake update
sudo darwin-rebuild switch --flake .#simple

Applying the same configuration on another machine

To apply the same configuration on another machine, you will need to:

  1. Install Nix as described above.
  2. sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake github:your-github-username/your-repository#your-config 3

  1. Nix names both a package manager and the language used for its configuration. NixOS is the Linux distribution built around them; nix-darwin applies a similar approach to macOS. Here we’ll focus on the package manager, nix-darwin, and the configuration language. ↩︎

  2. check the repository for a more up-to-date installation instruction. ↩︎

  3. replace your-github-username/your-repository with your repository, and your-config with the name of the entry in darwinConfigurations (simple in the example above). ↩︎