Setting up Nix Darwin on Mac Mini M1

tags

Introduction

Nix has been gaining significant attention in the Linux community, particularly for its innovative approach to package management and system configuration. While many are familiar with NixOS on Linux systems, fewer know about Nix Darwin, which brings similar capabilities to macOS. This guide was inspired mainly by the video Nix is my favorite package manager to use on macOS and will walk you through setting up Nix Darwin on a Mac Mini M1, transforming it from a standard macOS system into a declaratively managed development machine.

Prerequisites

Before we begin, you’ll need:

  • A Mac Mini with M1 chip
  • Administrative access to your machine
  • Basic familiarity with terminal commands
  • An Apple ID (for App Store installations)

Core Installation

Installing Nix Package Manager

First, we’ll install the Nix package manager, which forms the foundation of our setup. Open Terminal and run:

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

Follow the prompts to complete the installation process. This will set up the basic Nix environment on your system.

Setting up Nix Darwin

Nix Darwin extends Nix’s capabilities to manage macOS-specific features. Here’s how to set it up:

  1. Create the configuration directory:
mkdir -p ~/.config/nix
cd ~/.config/nix
  1. Initialize the Nix Darwin configuration with flakes:
nix flake init -t nix-darwin --extra-experimental-features "nix-command flakes"
sed -i '' "s/simple/$(scutil --get LocalHostName)/" flake.nix
  1. Install the configuration:
nix run nix-darwin --extra-experimental-features "nix-command flakes" -- switch --flake ~/.config/nix
  1. Verify the installation:
darwin-rebuild switch --flake ~/.config/nix

Adding Nix Homebrew Support

Nix Homebrew integration allows us to manage Homebrew packages declaratively. Add the following to your flake.nix:

{
  inputs = {
    nix-homebrew.url = "github:zhaofengli-wip/nix-homebrew";
    # ... other inputs
  };
 
  outputs = { self, nixpkgs, darwin, nix-homebrew, ... }: {
    darwinConfigurations."mac-mini" = {
      modules = [
        nix-homebrew.darwinModules.nix-homebrew
        {
          nix-homebrew = {
            enable = true;
            enableRosetta = true;
            user = "YOUR_USERNAME";
            autoMigrate = true;
          };
        }
      ];
    };
  };
}

Package Management Approaches

Nix Darwin offers several ways to install and manage software. Let’s explore each approach:

1. Nix Terminal Applications

For command-line tools, add them to your system packages in flake.nix:

environment.systemPackages = [
  pkgs.neovim
  # Add other packages here
];

2. GUI Applications

GUI applications require special handling to appear in Spotlight. Add this configuration to make them accessible:

environment.systemPackages = [
  pkgs.mkalias
];
 
system.activationScripts.applications.text = let
  env = pkgs.buildEnv {
    name = "system-applications";
    paths = config.environment.systemPackages;
    pathsToLink = "/Applications";
  };
in pkgs.lib.mkForce ''
  echo "setting up /Applications..." >&2
  rm -rf /Applications/Nix\ Apps
  mkdir -p /Applications/Nix\ Apps
  find ${env}/Applications -maxdepth 1 -type l -exec readlink '{}' + |
  while read -r src; do
    app_name=$(basename "$src")
    echo "copying $src" >&2
    ${pkgs.mkalias}/bin/mkalias "$src" "/Applications/Nix Apps/$app_name"
  done
'';

3. Non-Free Applications

Enable non-free applications by adding:

nixpkgs.config.allowUnfree = true;

4. Homebrew Applications

Manage Homebrew casks through your configuration:

homebrew = {
  enable = true;
  casks = [
    "firefox"
    # Add other casks here
  ];
  onActivation.cleanup = "zap";
  onActivation.autoUpdate = true;
  onActivation.upgrade = true;
};

5. App Store Applications

Install App Store applications using their ID:

homebrew = {
  masApps = {
    "The Unarchiver" = 425424353;
    # Add other apps here
  };
};

To find an app’s ID, either:

  • Install mas via Homebrew and use mas search "App Name"
  • Copy the ID from the App Store URL (e.g., from https://apps.apple.com/app/name/id425424353)

System Configuration

Command Line Tools

Add this to handle Xcode Command Line Tools installation:

system.activationScripts.postActivation.text = ''
  if ! /usr/bin/pgrep oahd >/dev/null 2>&1; then
    echo "Installing Rosetta 2..."
    /usr/bin/softwareupdate --install-rosetta --agree-to-license
  fi	  
 
  if ! /usr/bin/xcrun -f clang >/dev/null 2>&1; then
    echo "Installing Command Line Tools..."
    /usr/bin/xcode-select --install
  fi
'';

MacOS System Preferences

Customize macOS settings through your configuration:

system.defaults = {
  dock.autohide = true;
  # Add other preferences here
};

Maintenance

Updating Packages

To update Nix packages:

nix flake update

Homebrew packages will update automatically if you’ve enabled autoUpdate and upgrade in your configuration.

Best Practices

Version Control

Keep your Nix configuration under version control:

  1. Initialize a Git repository in ~/.config/nix
  2. Commit your changes regularly
  3. Consider backing up to a remote repository

Modularization

Split your configuration into logical modules by creating separate files in ~/.config/nix/modules/:

  • nix-core.nix: Core Nix settings
  • apps.nix: Application configurations
  • system.nix: System preferences

Then import them in your main configuration:

modules = [
  ./modules/nix-core.nix
  ./modules/apps.nix
  ./modules/system.nix
];

Troubleshooting

If you encounter the error path '/nix/store/.../file.nix' does not exist, ensure that:

  1. All new configuration files are tracked by Git
  2. Run git add on new files before rebuilding
  3. The file paths in your configuration are correct

Remember that while committing isn’t necessary, new files must at least be staged with Git for the Nix flake to recognize them.

Conclusion

With Nix Darwin set up on your Mac Mini M1, you now have a powerful, declarative way to manage your macOS system. This configuration provides reproducibility, version control, and easy system management across all aspects of your machine.