Nix is an incredibly powerful functional package manager and language focused on reproducable builds. For more information about Nix, check out this post.

In this guide there are 3 distinct components:

  • Nix: The underlying package manager and language
  • nix-darwin: MacOS specific configurations
  • home-manager: a framework centered around managing packages and dotfiles specific for your user

Step 1: Install Nix

sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume

If you are not using Nix to manage your dotfiles, you will need to make sure it’s included in your .zshrc:

source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
source /nix/var/nix/profiles/default/etc/profile.d/nix.sh

Step 2: Install nix-darwin

nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A installer
./result/bin/darwin-installer
rm -Rf result

Step 3: Install home-manager

nix-channel --add https://github.com/nix-community/home-manager/archive/release-21.05.tar.gz home-manager
nix-channel --update
nix-shell '<home-manager>' -A install

Step 4:

Create directory and file for home-manager config

mkdir -p ~/.config/nixpkgs && touch ~/.config/nixpkgs/home.nix

Step 5:

Edit your newly created home.nix configuration in your favorite editor. Here’s a sample of my config:

{ config, pkgs, ... }:

{
  # Let Home Manager install and manage itself.
  programs.home-manager.enable = true;

  # Home Manager needs a bit of information about you and the
  # paths it should manage.
  home.username = "grahammcintire";
  home.homeDirectory = "/Users/grahammcintire";

  # This value determines the Home Manager release that your
  # configuration is compatible with. This helps avoid breakage
  # when a new Home Manager release introduces backwards
  # incompatible changes.
  #
  # You can update Home Manager without changing this value. See
  # the Home Manager release notes for a list of state version
  # changes in each release.
  home.stateVersion = "21.11";

  home.packages = [
    pkgs.erlangR24
    pkgs.gcc
    pkgs.gnumake
    pkgs.readline
    pkgs.zlib
    pkgs.libxml2
    pkgs.ansible
    pkgs.autoconf
    pkgs.automake
    pkgs.awscli
    pkgs.axel
    pkgs.bat
    pkgs.cmake
    pkgs.ctags
    pkgs.curl
    pkgs.fish
    pkgs.fzf
    pkgs.gh
    pkgs.git
    pkgs.gnupg
    pkgs.graphviz
    pkgs.htop
    pkgs.hugo
    pkgs.imagemagick
    pkgs.ipcalc
    pkgs.mosh
    pkgs.jq
    pkgs.minikube
    pkgs.mosh
    pkgs.ngrok
    pkgs.openssl
    pkgs.pinentry
    pkgs.pgcli
    pkgs.plantuml
    pkgs.postgresql
    pkgs.pstree
    pkgs.python3
    pkgs.pv
    pkgs.silver-searcher
    pkgs.tasksh
    pkgs.taskwarrior
    pkgs.terraform
    pkgs.tldr
    pkgs.tmux
    pkgs.tree
    pkgs.vault
    pkgs.yarn
    pkgs.yq
    pkgs.zsh
  ];

  news.display = "silent";

  programs.bat.enable = true;
  programs.fzf.enable = true;
  programs.fzf.enableZshIntegration = true;
  programs.gh.enable = true;
}

To search for a package name, use nix search <name>.

After you save your configuration, have home-manager install and configure everything:

home-manager switch

What else?

Since nix creates reproducable builds, it also keeps a history of all packages and configurations as they change, allowing you to switch between them quickly!

nix-env --list-generations

To update installed packages:

nix-channel --update
home-manager switch

You can also use home-manager to generate all of your dotfiles for you. For instance, adding the following to home.nix will generate ~/.zshrc for you:

  programs.git = {
    enable = true;
    userEmail = "emailaddress";
    userName = "Graham McIntire";
    ignores = [ "*~" ".DS_Store" ];
    extraConfig = {
      url = {
        "git@github.com:" = {
          insteadOf = "https://github.com/";
        };
      };
      pull = {
        rebase = true;
      };
    };
  };