Declarative approach to setting up my Macbook workstation

So I have the habit of wiping my machine every 3-12 months (Pretty subjective window šŸ¤·) and I usually don’t have a hard time getting my workstation up and running again in an hour. I use chezmoi for my dotfiles configurations, homebrew for my package manager and oh-my-zsh + starship for my terminal prompt. Ansible is a fine tool to automate this process, but it feels a bit clunky running a playbook against macOS. This is where nix-darwin saves the day (or, well, a hour of my time). Creating one Nix flake allows me to declaratively install the packagesā€”CLI and GUIā€”onto my machine with a simple rebuild command. It also keeps my machine clean by ensuring I donā€™t have any lingering packages I forgot to delete when testing them out šŸ˜

Installing and configuring nix

I opted to use Determinate Systems’ installer to install Nix since it also comes with a handy uninstaller, just in case I change my mind about Nix. curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

Homebrew is also installed and managed by nix for Cask (GUI) applications and additional brews not found in nix /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

After that is installed, nix-darwin is next:

# Create a flake
mkdir -p ~/.config/nix-darwin
cd ~/.config/nix-darwin
nix flake init -t nix-darwin/master
sed -i '' "s/simple/$(scutil --get LocalHostName)/" flake.nix

# Install nix-darwin
nix run nix-darwin -- switch --flake ~/.config/nix-darwin

This will create flake.nix inside the ~/.config/nix-darwin directory and install the cli darwin-rebuild to build the flake within macOS

Below is my current config, which will change over time, but it’s great to have it declaratively set within one file. :-)

What I have set

  • Install System packages
  • Install Nerd Fonts
  • Install and manage homebrew packages and casks
    • Zap/Remove anything brews not configured with Nix
  • Use touch ID for sudo commands
{
  description = "digitalsoba's nix-darwin system flake";
  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, ... }: {
      nixpkgs.config.allowUnfree = true;
      environment.systemPackages =
        [ 
          pkgs.chezmoi
          pkgs.fastfetch
          pkgs.kind
          pkgs.kubernetes-helm
          pkgs.kubectx
          pkgs.k3d
          pkgs.k9s
          pkgs.mise
          pkgs.neovim
          pkgs.starship
          pkgs.tmux
          pkgs.zsh
        ];
      fonts.packages = with pkgs; [
        nerd-fonts.fira-code
        nerd-fonts.jetbrains-mono
      ];
      homebrew = {
        enable = true;
        brews = [
          "antidote"
          "mas"
        ];
        casks = [
          "alfred"
          "brave-browser"
          "bruno"
          "discord"
          "firefox"
          "ghostty"
          "iina"
          "jordanbaird-ice"
          "orbstack"
          "obsidian"
          "rectangle"
          "spotify"
          "the-unarchiver"
          "zen-browser"
          "zoom"
        ];
        onActivation.cleanup = "zap";
        onActivation.autoUpdate = true;
        onActivation.upgrade = true;
      };
      # Necessary for using flakes on this system.
      nix.settings.experimental-features = "nix-command flakes";

      # 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 = 5;

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

      # System wide configuration options.
      security.pam.enableSudoTouchIdAuth = true;
    };
  in
  {
    # Build darwin flake using:
    # $ darwin-rebuild build --flake .#mysystem
    darwinConfigurations."mysystem" = nix-darwin.lib.darwinSystem {
      modules = [
        configuration
        nix-homebrew.darwinModules.nix-homebrew 
        {
          nix-homebrew = {
            enable = true;
            enableRosetta = true;
            user = "digitalsoba";
            autoMigrate = true;
          };
        }
      ];
    };
  };
}

Applying changes

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

And in a few minutes, my system is configured and ready to go

Whats Next?

  • Set-up nix on my Framework Laptop running project bluefin
  • Explore Nix home-manager
  • Maybe NixOS in my homelab???