How Home Manager Replaces Homebrew and Dotfiles
The traditional Mac developer setup involves Homebrew for packages and a dotfiles repository for configuration. Home Manager offers a unified, declarative alternative.
The Traditional Mac Dev Setup
A typical workflow:
- Install Homebrew
- Run
brew install git vim tmux ... - Clone your dotfiles repo
- Run an install script that symlinks configs
Over time:
- Packages drift between machines
- The dotfiles repo grows scripts to handle edge cases
- "Did I install that with brew or was it a manual install?"
- Configuration changes happen ad-hoc, sometimes forgotten
What Home Manager Provides
Home Manager manages your user environment through Nix:
# home.nix
{ pkgs, ... }: {
home.packages = [
pkgs.git
pkgs.vim
pkgs.tmux
pkgs.ripgrep
pkgs.fd
];
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "you@example.com";
extraConfig = {
init.defaultBranch = "main";
pull.rebase = true;
};
};
programs.zsh = {
enable = true;
shellAliases = {
ll = "ls -la";
gs = "git status";
};
};
}Apply with home-manager switch. Everything updates atomically.
Migration Path
Step 1: Install Nix
Use the Determinate Systems installer (see docs):
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- installStep 2: Enable Home Manager
Add to your flake or install standalone:
# flake.nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, home-manager, ... }: {
homeConfigurations."youruser" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.aarch64-darwin;
modules = [ ./home.nix ];
};
};
}Step 3: Migrate Packages Gradually
Start with packages you use daily. Don't try to migrate everything at once.
# List what Homebrew has installed
brew list
# Add equivalents to home.packages
# Remove from Homebrew as you verify they work
brew uninstall <package>Step 4: Convert Dotfiles to Nix
Instead of symlinked dotfiles:
# Old: ~/.config/git/config symlinked from dotfiles repo
# New: Generated by Home Manager
programs.git = {
enable = true;
# ... configuration
};
# For files without native HM support:
home.file.".config/some-app/config.toml".text = ''
setting = "value"
'';Advantages Over Brew + Dotfiles
Single Source of Truth
One repository, one command to apply. No separate package list and config repo to keep in sync.
Atomic Updates
home-manager switch either succeeds completely or fails completely. No half-applied states.
Easy Machine Migration
Clone your config, run home-manager switch. New machine matches old machine exactly.
Version-Pinned Dependencies
Flake lock files pin exact versions. Update when you choose, not when upstream decides.
Rollback
Previous generations remain available. Something break? home-manager generations lists history.
Coexistence Strategy
You don't have to go all-in immediately:
- Keep Homebrew for GUI apps (or explore nix-darwin for those too)
- Use Home Manager for CLI tools and configs
- Migrate incrementally as you gain confidence
The goal is reducing configuration drift and gaining reproducibility, not religious purity.