GuideFebruary 9, 20276 min read

ConfigSync + Homebrew Bundle: The Ultimate Mac Reproducibility Stack

ConfigSync syncs your configs and secrets. Homebrew Bundle installs your packages. Together, one command restores your entire Mac environment.

The Two Halves of a Dev Environment

A development environment has two halves: the software you install and the configuration you apply to it. Homebrew Bundle handles the first half brilliantly. Your Brewfile declares every formula, cask, tap, and Mac App Store application you need. Run brew bundle and everything installs.

But installing software is only half the battle. After Homebrew installs VS Code, you still need your settings, keybindings, and extensions. After it installs git, you need your gitconfig and aliases. After it installs your shell, you need your zshrc, aliases, and custom functions. That is where ConfigSync comes in.

Together, they form the closest thing to Nix-level reproducibility without the complexity of learning a functional programming language to configure your laptop.

How the Homebrew Module Works

ConfigSync's homebrew module automatically captures your installed packages as a Brewfile. This means you do not have to maintain the Brewfile manually. Every time you push, ConfigSync runs brew bundle dump and includes the result.

Enable the Homebrew module
# Enable the Homebrew module $ configsync enable module homebrew # This captures your Brewfile automatically on push: $ configsync push -m "Updated environment" Scanning modules... homebrew: Captured 47 formulae, 12 casks, 3 taps ...

The generated Brewfile is a plain text manifest of everything Homebrew manages on your machine:

Auto-generated Brewfile
# Generated by ConfigSync homebrew module tap "homebrew/bundle" tap "oven-sh/bun" # Core development tools brew "git" brew "gh" brew "node" brew "python@3.12" brew "go" # CLI utilities brew "ripgrep" brew "fd" brew "jq" brew "bat" brew "eza" brew "fzf" brew "zoxide" brew "tmux" brew "starship" # Applications cask "visual-studio-code" cask "iterm2" cask "docker" cask "1password" cask "raycast" cask "arc"

The Post-Pull Hook

The magic happens when you pull on a new machine. ConfigSync restores your configs and then runs a post-pull hook that triggers Homebrew Bundle to install all your packages.

Set up the post-pull hook
# Configure the post-pull hook $ configsync config set hooks.post-pull \ "brew bundle --file=~/.Brewfile --no-upgrade" # Now when you pull on a new machine: $ configsync pull Restoring from snapshot... ✓ shell config ✓ git config ✓ ssh keys (encrypted) ✓ vscode settings + extensions ✓ homebrew Brewfile ... Running post-pull hook... brew bundle: Installing 47 formulae... brew bundle: Installing 12 casks... brew bundle: 59 packages installed. Done. Environment fully restored.
The --no-upgrade flag prevents Homebrew from upgrading existing packages during bundle install. This keeps the operation fast and predictable. You can upgrade separately when you are ready.

What Each Tool Handles

CategoryHomebrew BundleConfigSync
CLI tools (git, node, ripgrep)InstallsConfigures
GUI apps (VS Code, Docker)InstallsConfigures
Shell (zsh)Installs plugins (via brew)Configures (.zshrc, aliases)
SSH keysN/ARestores (encrypted)
Environment variablesN/ARestores (encrypted)
Package manager auth (.npmrc)N/ARestores (encrypted)
Brewfile itselfReadsSyncs across machines

The division is clean: Homebrew Bundle handles package installation, ConfigSync handles everything else. There is no overlap or conflict between the two tools.

Keeping the Brewfile in Sync

One common frustration with Homebrew Bundle is keeping the Brewfile up to date. You install a new formula with brew install and forget to add it to the Brewfile. ConfigSync eliminates this problem by regenerating the Brewfile on every push.

Automatic Brewfile updates
# Install a new tool $ brew install lazygit # Push to ConfigSync (Brewfile auto-updates) $ configsync push -m "Added lazygit" homebrew: Detected new formula: lazygit Updated Brewfile (48 formulae, 12 casks) # On your other machine, pull: $ configsync pull ... Running post-pull hook... brew bundle: Installing lazygit... done.

Cleaning Up with Brew Bundle

Homebrew Bundle also supports cleanup: removing packages that are not in the Brewfile. This is useful for keeping machines consistent and free of accumulated cruft.

Cleanup unused packages
# See what would be removed $ brew bundle cleanup --file=~/.Brewfile Would uninstall: wget (not in Brewfile) tree (not in Brewfile) # Actually remove them $ brew bundle cleanup --file=~/.Brewfile --force # Or add the cleanup to your post-pull hook: $ configsync config set hooks.post-pull \ "brew bundle --file=~/.Brewfile --no-upgrade && brew bundle cleanup --file=~/.Brewfile --force"

The One-Command Full Restore

With ConfigSync and Homebrew Bundle working together, restoring your entire Mac environment is genuinely a single command. ConfigSync pulls your configs, triggers Homebrew Bundle to install your software, and runs any additional bootstrap scripts you have defined.

This is the promise Nix makes but with dramatically less complexity. You do not need to learn a new language or manage a Nix store. You keep using Homebrew the way you always have. You keep your config files where they normally live. ConfigSync and Homebrew Bundle just make them reproducible.

The next time you unbox a new Mac, the entire setup is: install Homebrew, install ConfigSync, run configsync pull. Everything else is automatic.

Ready to try ConfigSync?

Sync your entire dev environment across machines in minutes. Free forever for up to 3 devices.