• Kloudnative
  • Posts
  • Is Nix a Better Alternative to Homebrew for Mac Package Management?

Is Nix a Better Alternative to Homebrew for Mac Package Management?

Explore how Nix enhances package management and system configuration on macOS, offering a more streamlined and flexible solution.

In partnership with

When you search online, you'll often see that:

  • Nix is a purely functional programming language.

  • Nix is a package manager.

  • Nix (Nixpkgs) is a package repository with package definitions written in the Nix language.

  • NixOS is a Linux distribution.

In this article, I'll show you how I'm using the Nix package manager and Nixpkgs repository to install packages and configure my new Mac.

Why Nix?

After a week with Nix, I've found it's excellent for installing CLI packages, Mac applications, settings, and more in a declarative way. This means you can pull your .dotfiles from GitHub, and let Nix configure everything for you automatically.

Nix supports many packages (nixos.org/packages) and, best of all, you can reuse your Nix config across other OS environments.

Kloudnative is committed to staying free for all our users. We kindly encourage you to explore our sponsors to help support us.

There’s a reason 400,000 professionals read this daily.

Join The AI Report, trusted by 400,000+ professionals at Google, Microsoft, and OpenAI. Get daily insights, tools, and strategies to master practical AI skills that drive results.

☝️ Support Kloudnative by clicking the link above to explore our sponsors!

Install Nix

To install Nix on your Mac, run:

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

For more detailed information, visit nixos.org/download.

A unique feature of Nix is that it doesn't install packages directly into your main system. Instead, it creates a separate volume, making it easy to add or remove packages. Nix also supports versioning, so you can roll back if something goes wrong — more on that in a future article.

To verify your installation, try running neofetch with nix-shell.

Let's try another command — what if we run neofetch on its own? I'll leave it to you to explore. As I mentioned earlier, Nix doesn't install packages directly onto your machine.

Configure Nix

Now let's configure Nix. Start by creating the config directory:

mkdir ~/.config/nix

We'll use nix-darwin with flakes. You can learn more about it on GitHub.

Run this command to initialize the flake.nix file:

cd ~/.config/nix
nix flake init -t nix-darwin - extra-experimental-features "nix-command flakes"

Once flake.nix is created, open it in your editor of choice. I use Vim, but feel free to use your favorite IDE.

If you're on Apple Silicon, update the line:

nixpkgs.hostPlatform = "x86_64_darwin"

to

nixpkgs.hostPlatform = "aarch64-darwin"

This change is necessary to ensure compatibility with Apple Silicon.

To install nix-darwin, run:

nix run nix-darwin --extra-experimental-features "nix-command flakes" -- switch --flake ~/.config/nix#m3mac

You can check if it's installed by running which darwin-rebuild:

After the installation, apply any system changes by running:

darwin-rebuild switch - flake ~/.config/nix

Make sure to re-run darwin-rebuild whenever you add more packages or configurations to flake.nix.

Installing Packages

Visit search.nixos.org/packages to find the packages you need. In future articles, I'll dive deeper into configuring flake.nix to install Mac applications, specific Brew packages, and customize Mac settings.

Let's dive deeper into the fluxe.nix file

My config file

You can check my config file here: [.nix]

{
  description = "Chris Darwin Nix Configuration";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    nix-darwin.url = "github:LnL7/nix-darwin";
    nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
    nix-homebrew.url = "github:zhaofengli-wip/nix-homebrew";
  };
  outputs = inputs@{ self, nix-darwin, nixpkgs, nix-homebrew }:
  let
    configuration = { pkgs, config, ... }: {
      nixpkgs.config.allowUnfree = true;
      # List packages installed in system profile. To search by name, run:
      # $ nix-env -qaP | grep wget
      environment.systemPackages =
        [
          pkgs.go
          pkgs.rustup
          pkgs.oh-my-posh
          pkgs.alacritty
          pkgs.mkalias
          pkgs.neovim
          pkgs.tmux
          pkgs.neofetch
          pkgs.git
        ];
      homebrew = {
        enable = true;
        taps = [
"FelixKratz/formulae"
        ];
        brews = [
          "mas"
          "borders"
        ];
        casks = [
          #"the-unarchiver"
          "sf-symbols"
          "font-hack-nerd-font"
        ];
        masApps = {
          #"Yoink" = 457622435;
        };
        onActivation.cleanup = "zap";
      };
      fonts.packages = [
        (pkgs.nerdfonts.override { fonts = [ "JetBrainsMono" ]; })
      ];
      system.activationScripts.applications.text = let
        env = pkgs.buildEnv {
          name = "system-applications";
          paths = config.environment.systemPackages;
          pathsToLink = "/Applications";
        };
      in
        pkgs.lib.mkForce ''
          # Set up applications.
          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 src; do
            app_name=$(basename "$src")
            echo "copying $src" >&2
            ${pkgs.mkalias}/bin/mkalias "$src" "/Applications/Nix Apps/$app_name"
          done
        '';
      system.defaults = {
        dock.autohide  = true;
        dock.persistent-apps = [
          "/System/Applications/Launchpad.app/"
          "${pkgs.alacritty}/Applications/Alacritty.app"
          "/System/Applications/Mail.app"
          "/System/Applications/Calendar.app"
          "/Applications/Slack.app"
          "/Applications/Notion.app"
          "/Applications/Brave Browser.app"
        ];
        finder.FXPreferredViewStyle = "clmv";
        loginwindow.GuestEnabled  = false;
        NSGlobalDomain.AppleICUForce24HourTime = true;
        NSGlobalDomain.AppleInterfaceStyle = "Dark";
        NSGlobalDomain.KeyRepeat = 2;
      };
      # Auto upgrade nix package and the daemon service.
      services.nix-daemon.enable = true;
      # nix.package = pkgs.nix;
      # 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;
      # The platform the configuration will be used on.
      nixpkgs.hostPlatform = "aarch64-darwin";
    };
  in
  {
    # Build darwin flake using:
    # $ darwin-rebuild build --flake .#m3mac
    darwinConfigurations."m3mac" = nix-darwin.lib.darwinSystem {
      modules = [
        configuration
        nix-homebrew.darwinModules.nix-homebrew
        {
          nix-homebrew = {
            enable = true;
            # Apple Silicon Only
            enableRosetta = true;
            # User owning the Homebrew prefix
            user = "chris";
            autoMigrate = true;
          };
        }
      ];
    };
    # Expose the package set, including overlays, for convenience.
    darwinPackages = self.darwinConfigurations."m3mac".pkgs;
  };
}

1. Top-Level Metadata

description = "Chris Darwin Nix Configuration";

This simple line provides a description, useful for identifying what this configuration is for, especially if you're managing multiple configurations.

2. Input Sources

inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    nix-darwin.url = "github:LnL7/nix-darwin";
    nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
    nix-homebrew.url = "github:zhaofengli-wip/nix-homebrew";
  };

This section defines external inputs, which are other repositories that contain Nix package definitions and configurations:

  • nixpkgs: A collection of Nix packages (pointed to the "unstable" branch for the latest versions).

  • nix-darwin: Adds support for macOS-specific configurations.

  • nix-homebrew: Integrates Homebrew, allowing you to install macOS apps not available in Nixpkgs.

3. Package Installation

environment.systemPackages = [
  pkgs.go
  pkgs.rustup
  pkgs.oh-my-posh
  pkgs.alacritty
  pkgs.mkalias
  pkgs.neovim
  pkgs.tmux
  pkgs.neofetch
  pkgs.git
];

The systemPackages list contains essential packages that will be installed on your system. This setup includes programming languages (Go, Rust), CLI tools (Neovim, Tmux, Git), and system utilities (Neofetch, Alacritty).

4. Homebrew Integration

homebrew = {
  enable = true;
  taps = [ "FelixKratz/formulae" ];
  brews = [ "mas" "borders" ];
  casks = [ "sf-symbols" "font-hack-nerd-font" ];
  masApps = { };
};

This section enables Homebrew integration:

  • Taps: Additional repositories.

  • Brews: CLI packages to install via Homebrew.

  • Casks: GUI applications and fonts.

  • masApps: Apps from the Mac App Store, customizable by specifying app IDs.

5. Font Configuration

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

This block installs JetBrains Mono from the Nerd Fonts library, optimized for development and compatible with several terminals.

6. System Defaults

system.defaults = {
  dock.autohide  = true;
  dock.persistent-apps = [ /* App list */ ];
  finder.FXPreferredViewStyle = "clmv";
  loginwindow.GuestEnabled  = false;
  NSGlobalDomain.AppleICUForce24HourTime = true;
  NSGlobalDomain.AppleInterfaceStyle = "Dark";
  NSGlobalDomain.KeyRepeat = 2;
};

This section defines system-wide settings:

  • dock.autohide: Hides the Dock automatically.

  • persistent-apps: Keeps frequently-used apps in the Dock.

  • Finder and UI settings: Sets Finder's view style, enables dark mode, enforces 24-hour time, and adjusts key repeat speed.

7. Nix Daemon Service

services.nix-daemon.enable = true;

This enables the Nix daemon, allowing root and non-root users to manage Nix packages in parallel.

8. Experimental Features

nix.settings.experimental-features = "nix-command flakes";

9. Zsh Shell Configuration

programs.zsh.enable = true;

Enables Zsh as the default shell, especially useful on macOS Catalina or later where Zsh is the default.

10. System Information

system.configurationRevision = self.rev or self.dirtyRev or null;
system.stateVersion = 4;
nixpkgs.hostPlatform = "aarch64-darwin";

These options provide version control:

  • configurationRevision: Tracks configuration changes.

  • stateVersion: Sets the system state version.

  • hostPlatform: Specifies Apple Silicon compatibility.

11. Building and Running the Configuration

darwinConfigurations."m3mac" = nix-darwin.lib.darwinSystem {
  modules = [ configuration nix-homebrew.darwinModules.nix-homebrew ];
};

This wraps everything up, defining the configuration for m3mac and specifying it as a Nix-Darwin system. You can build it with:

darwin-rebuild switch - flake ~/.config/nix