Back to Blog

Nix-Darwin: My favorite package manager for macOS

Dreams of Code Logo
Dreams of Code
May 15, 2025
Nix-Darwin: My favorite package manager for macOS

Nix-Darwin: The Ultimate macOS System Management Tool

Nix-Darwin on macOS

Introduction

Recently, I've been traveling frequently and using my MacBook Pro instead of my Linux laptops. While the battery life and performance are fantastic, there are certain aspects of macOS that required adjustment, particularly the lack of a default package manager.

Although Homebrew is the most popular third-party package manager for macOS, I've discovered a significantly more powerful alternative: Nix with nix-darwin.

The Power of Nix-Darwin

Imagine having a fresh macOS installation on a new machine. By running just three commands—to download the package manager, fetch your configuration, and install it—you can transform that vanilla system into your perfectly customized environment:

  • All your favorite GUI applications installed (Firefox, Obsidian, even Mac App Store apps like Yoink)
  • Terminal emulator (Alacritty) set up with your preferred configuration and color scheme
  • Shell environment (Zsh) running your custom configuration
  • CLI applications (Tmux, Zoxide, Neovim) installed with their respective dotfiles
  • System settings configured to your liking (key repeat behavior, Finder defaults, Dock settings)

This is the power of the Nix package manager with nix-darwin, and it has completely transformed how I work on macOS.

What Makes Nix Special?

Nix allows you to specify every application, package, system setting, and configuration on your machine in a declarative manner. This includes:

  • CLI tools from the Nix repositories
  • Applications from the Mac App Store
  • Apps from Homebrew

The configuration is written in a functional language called Nix, which offers tremendous expressiveness (similar to using Lua for Neovim configuration).

The advantages of this approach include:

  1. Reproducibility: Easily replicate your setup across multiple machines and operating systems
  2. Consistency: Keep your MacBook and Linux laptops in sync with identical packages and configurations
  3. Version Control: Store your entire system configuration in Git, gaining all the benefits of source control
  4. Rollback Capability: Easily revert to previous configurations if something goes wrong

Getting Started with Nix and Nix-Darwin

While Nix offers significant advantages over Homebrew, it does have a steeper learning curve. The declarative approach to managing packages and configurations can be challenging to adapt to initially.

Here's how to set up Nix and nix-darwin on your macOS system:

Step 1: Install the Nix Package Manager

First, you'll need to install the Nix package manager itself. Visit the NixOS website and copy the macOS installation command:

bash
sh <(curl -L https://nixos.org/nix/install)

This installer will create a separate Apple volume and several user accounts. Once installation is complete, verify it's working by running:

bash
nix-shell -p neofetch --run neofetch

The beauty of the nix-shell command is that it downloads packages and loads them into a temporary environment without permanently installing them. This allows you to run one-off commands without bloating your system.

Step 2: Set Up Nix-Darwin with Flakes

Create a directory to store your Nix configuration:

bash
mkdir -p ~/nix
cd ~/nix

Initialize your nix-darwin configuration with flakes support:

bash
nix run --extra-experimental-features "nix-command flakes" \
  github:LnL7/nix-darwin/master -- init

This will create a flake.nix file in your directory. Open it in your text editor.

Step 3: Configure Your Flake

The flake.nix file consists of two main sections:

  1. Inputs: Dependencies for your flake
  2. Outputs: What the flake produces (in this case, your system configuration)

Here's what to customize:

  • Change the description to something personal
  • Ensure the architecture is correct (aarch64-darwin for Apple Silicon, x86_64-darwin for Intel)
  • Update the configuration name (I prefer using a machine-specific name like "mini" for my Mac Mini)

Step 4: Build and Apply Your Configuration

Run the following command to build and apply your nix-darwin configuration:

bash
nix run --extra-experimental-features "nix-command flakes" \
  github:LnL7/nix-darwin/master -- switch --flake ~/nix#mini

Verify the installation with:

bash
which darwin-rebuild

Package Management with Nix

Installing CLI Applications

Unlike traditional package managers where you run commands to install packages, Nix takes a declarative approach. To install packages:

  1. Edit your flake.nix file
  2. Find the environment.systemPackages list
  3. Add the packages you want to install
  4. Rebuild your configuration with:
bash
darwin-rebuild switch --flake ~/nix#mini

For example, to install Neovim:

nix
environment.systemPackages = [
  pkgs.neovim
  pkgs.tmux
];

Finding Available Packages

Discover available packages using the nix search command:

bash
nix search nixpkgs tmux

Alternatively, visit search.nixos.org to browse packages.

Installing GUI Applications

Nix can install GUI applications in several ways:

1. From the Nix Repository

Add the application to your environment.systemPackages list:

nix
environment.systemPackages = [
  pkgs.neovim
  pkgs.alacritty
];

However, applications installed this way won't appear in Spotlight by default, as macOS doesn't index symlinks. To fix this, add the following to your configuration:

nix
system.activationScripts.applications.text = ''
  echo "setting up ~/Applications/Nix Apps..."
  mkdir -p ~/Applications/Nix\ Apps
  for app in $(find ${config.system.build.applications}/Applications -maxdepth 1 -type l); do
    src="$(/usr/bin/stat -f%Y "$app")"
    cp -r "$src" ~/Applications/Nix\ Apps
  done
'';

2. Installing Fonts

Fonts can be installed declaratively by adding them to the fonts.fonts list:

nix
fonts.fonts = [
  (pkgs.nerdfonts.override { fonts = [ "JetBrainsMono" ]; })
];

3. Using Homebrew for Additional Applications

For applications not available in the Nix repository, integrate Homebrew:

  1. Add the nix-homebrew module to your inputs:
nix
inputs = {
  nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
  darwin.url = "github:lnl7/nix-darwin";
  nix-homebrew.url = "github:zhaofengli-wip/nix-homebrew";
};
  1. Add the module to your configuration:
nix
homebrew = {
  enable = true;
  onActivation.cleanup = "zap"; # Remove packages not in config
  casks = [
    "hammerspoon"
    "firefox"
    "iina"
    "the-unarchiver"
  ];
  brews = [
    "mas" # Mac App Store CLI
  ];
  masApps = {
    "Yoink" = 457622435;
  };
};

Updating Packages

To update your packages:

  1. Update the flake inputs:
bash
nix flake update
  1. Rebuild your configuration:
bash
darwin-rebuild switch --flake ~/nix#mini

For Homebrew packages, set the following options to automatically update:

nix
homebrew = {
  enable = true;
  onActivation.autoUpdate = true;
  onActivation.upgrade = true;
  # ...
};

Configuring macOS System Settings

One of the most powerful features of nix-darwin is the ability to declaratively configure macOS system settings.

For example, to auto-hide the Dock:

nix
system.defaults.dock.autohide = true;

Other useful settings include:

nix
system.defaults = {
  dock = {
    autohide = true;
    persistent-apps = [
      "/etc/profiles/per-user/username/bin/alacritty.app"
      "/Applications/Firefox.app"
      "/Applications/Obsidian.app"
      "/System/Applications/Mail.app"
      "/System/Applications/Calendar.app"
    ];
  };
  finder.FXPreferredViewStyle = "clmv"; # Column view
  loginwindow.GuestEnabled = false;
  NSGlobalDomain = {
    AppleICUForce24HourTime = true;
    AppleInterfaceStyle = "Dark";
    KeyRepeat = 1; # Fastest
    InitialKeyRepeat = 15;
  };
};

To discover available options:

  1. Use the darwin-help command
  2. Check the mynixos.com documentation
  3. Explore the nix-darwin manual pages

Version Control and Backup

For safety and reproducibility, commit your Nix configuration to a Git repository:

bash
cd ~/nix
git init
git add .
git commit -m "Initial nix-darwin configuration"

Consider pushing to a remote repository for backup and easier deployment across multiple machines.

Conclusion

Nix with nix-darwin offers a powerful approach to managing your macOS system. While the learning curve is steeper than traditional package managers, the benefits of declarative, reproducible system configuration are significant.

The ability to completely rebuild your working environment from a single configuration file is invaluable, especially if you work across multiple machines or occasionally need to set up a new system.

In future posts, I'll explore how to manage dotfiles declaratively using the Home Manager module, further enhancing the reproducibility of your development environment.


If you're interested in learning more about Nix, check out&#32;Vimjoy's YouTube channel, which offers excellent content on setting up Nix and NixOS that translates well to nix-darwin.

Resources

Get Support