Reproducible macOS Configurations with Nix

What is Nix?
Nix is a package manager 1 for Linux and macOS. Together with nix-darwin, it lets you describe much of your macOS setup in a configuration file. A flake can pin dependencies, making that setup easier to reproduce on another machine.
A key component of the Nix ecosystem is Nixpkgs, the default repository of Nix packages, which contains thousands of searchable packages that you can easily integrate into your configuration.
Why Use Nix on macOS?
If you use macOS, you may already know Homebrew as a package manager. Nix also manages packages; with nix-darwin, it can manage system settings as well.
It offers many advanced features, such as binary caching, package pinning, package overrides, automatic runtime dependencies, and custom package sources.
One particularly interesting part of the Nix ecosystem for macOS users is nix-darwin, a set of tools and configurations that allows you to manage your macOS system configuration using Nix. This is what we will go over in this post.
Installing Nix
There are multiple ways to install Nix on your macOS, but I found that the most reliable way is to use the DeterminateSystems/nix-installer script by running 2
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- installSetting up nix-darwin
Once you have Nix installed, you can set up nix-darwin by running the following command:
# Create and navigate to a new directory for your nix-darwin configuration
mkdir -p ~/.config/nix-darwin-config
cd ~/.config/nix-darwin-config
# initialize a new nix flake with the nix-darwin template
nix flake init -t nix-darwin/masterThis last command will create a flake.nix file in your nix-darwin-config directory. This file is where you will define your system configuration. You will want to change the following configuration options:
nixpkgs.hostPlatformtoaarch64-darwin(for Apple Silicon) orx86_64-darwin(for Intel Macs).nixpkgs.config.allowUnfreetotrueif you want to allow unfree packages (e.g. steam).
you can also add any system package you need in the environment.systemPackages array, for example:
environment.systemPackages =
[ pkgs.vim
pkgs.pet
pkgs.lazygit
pkgs.lazydocker
pkgs.fzf
];This is a minimal configuration to get you started. You can add more packages and customizations as needed. A full list of available options can be found in the nix-darwin manual.
I like to keep my configuration in a git repository, along with my dotfiles, so I can easily share it across machines.
Your configuration file will probably look something like this:
{
description = "Victor's MacOS Nix System flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nix-darwin.url = "github:nix-darwin/nix-darwin/master";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = inputs@{ self, nix-darwin, nixpkgs }:
let
configuration = { pkgs, ... }: {
# List packages installed in system profile. To search by name, run:
# $ nix-env -qaP | grep wget
environment.systemPackages =
[ pkgs.vim
pkgs.fzf # Fuzzy finder
pkgs.pet # Snippet manager
pkgs.lazydocker # Docker TUI
pkgs.lazygit # Git TUI
pkgs.ripgrep # Faster grep
pkgs.zoxide # Directory jump tool (z)
pkgs.pyenv # Python version manager
pkgs.gh # GitHub CLI
pkgs.go # Go programming language
pkgs.kubectl # Kubernetes CLI
pkgs.kubectx # Kubernetes context switcher
pkgs.tenv # OpenTofu, Terraform, Terragrunt and Atmos version manager
pkgs.delta # Terminal git diff viewer with syntax highlighting
pkgs.curl # Command line tool for transferring data with URL syntax
pkgs.jq # Command line JSON processor
pkgs.yq # Command line YAML processor
pkgs.just # Command runner, similar to make
pkgs.vscode # Visual Studio Code
pkgs.neovim # Vim-fork focused on extensibility and usability
pkgs.helix # Vim-like modal text editor
pkgs.k9s # Kubernetes CLI to manage your clusters in styles
pkgs.pandoc # Universal document converter
pkgs.python3 # Python 3 programming language
pkgs.rustc # Rust programming language
pkgs.rustup # Rust toolchain installer
pkgs.lorri # Nix shell manager
pkgs.htop # Interactive process viewer
pkgs.tree # Display directories as trees
pkgs.jetbrains-mono # JetBrains Mono font
pkgs.ffmpeg # Multimedia framework
pkgs.alacritty # GPU-accelerated terminal emulator
];
# allowUnfree is required to install some packages that are not "free" software.
nixpkgs.config.allowUnfree = true;
# 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;
system.defaults = {
dock.autohide = true;
dock.mru-spaces = false; # Most Recently Used spaces.
finder.AppleShowAllExtensions = true;
finder.FXPreferredViewStyle = "icnv"; # icon view. Other options are: Nlsv (list), clmv (column), Flwv (cover flow)
screencapture.location = "~/Pictures/screenshots";
screensaver.askForPasswordDelay = 10; # in seconds
};
# The platform the configuration will be used on.
nixpkgs.hostPlatform = "aarch64-darwin";
nix.extraOptions = ''
extra-platforms = x86_64-darwin aarch64-darwin
'';
};
in
{
# Build darwin flake using:
# $ darwin-rebuild build --flake .#simple
darwinConfigurations."simple" = nix-darwin.lib.darwinSystem {
modules = [ configuration ];
};
# Expose the package set, including overlays, for convenience.
darwinPackages = self.darwinConfigurations."simple".pkgs;
};
}Applying the Configuration
To apply your configuration, run the following command:
sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake .#simpleTo update your configuration, you can run:
nix flake update
sudo darwin-rebuild switch --flake .#simpleApplying the same configuration on another machine
To apply the same configuration on another machine, you will need to:
- Install Nix as described above.
sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake github:your-github-username/your-repository#your-config3
Nix names both a package manager and the language used for its configuration. NixOS is the Linux distribution built around them; nix-darwin applies a similar approach to macOS. Here we’ll focus on the package manager, nix-darwin, and the configuration language. ↩︎
check the repository for a more up-to-date installation instruction. ↩︎
replace
your-github-username/your-repositorywith your repository, andyour-configwith the name of the entry indarwinConfigurations(simplein the example above). ↩︎