Overview

This guide walks through my automated macOS setup and backup workflow. The goals:

  • Quickly set up a new Mac by running a single bootstrap script.
  • Avoid manually remembering installed apps, preferences, and configs.
  • Automatically back up my dotfiles and Homebrew installs to a GitHub repo.
  • Include App Store apps, macOS preferences, and even my AirPrint printer.

Core Components

1. Homebrew Bundle

I use Homebrew’s brew bundle dump to capture all my brew formulas, casks, and MAS (Mac App Store) apps into a Brewfile.

Example Brewfile excerpt:

brew "bat"
brew "zsh"
brew "zsh-autosuggestions"
brew "zsh-syntax-highlighting"
cask "1password"
cask "iterm2"
cask "visual-studio-code"

# Mac App Store apps 
mas "Magnet", id: 441258766
mas "AdGuard for Safari", id: 1440147259
mas "1Password for Safari", id: 1569813296
mas "Hush Nag Blocker", id: 1544743900

This file lives in my GitHub repo so it’s always up-to-date.


2. Dotfiles with GNU Stow

I store my dotfiles in version control and use GNU Stow to symlink them into place.

Example directory structure:

~/GitHub Repos/macOS/
  ├── zsh/.zshrc
  ├── ssh/config
  ├── Brewfile
  ├── bootstrap.sh
  └── capture-macos

Stow command used in bootstrap:

stow --adopt -v -t "$HOME" zsh ssh

The --adopt flag moves any existing local files into the repo so they are backed up.


3. macOS Preferences

I capture my actual system preferences into a script using defaults commands so they can be restored later.

Example capture-macos snippet:

defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -int 1
defaults write com.apple.dock minimize-to-application -bool 1
defaults write com.apple.dock tilesize -int 81
killall Finder Dock SystemUIServer 2>/dev/null || true

4. Printer Installation

I even automated my printer setup so I don’t have to manually add it.

Example AirPrint addition:

sudo lpadmin -p "EPSON_ET_2760"              -E              -v "dnssd://EPSON%20ET-2760._ipps._tcp.local./?uuid=cfe92100-6234-1234-a45f-50579ca926aa"              -m everywhere   && sudo cupsenable "EPSON_ET_2760"   && sudo cupsaccept "EPSON_ET_2760"   && lpoptions -d "EPSON_ET_2760"

This uses the printer’s UUID so it works even if its IP changes.


The Bootstrap Script

The heart of this setup is bootstrap.sh.
It:

  1. Installs everything from the Brewfile (including MAS apps).
  2. Links dotfiles with Stow.
  3. Restores macOS preferences.
  4. Installs my printer.
  5. Reminds me to do manual steps like enabling 1Password’s SSH agent.

Example excerpt:

#!/usr/bin/env bash
set -euo pipefail

echo "==== Installing packages from Brewfile... ===="
brew bundle --file="$HOME/GitHub Repos/macOS/Brewfile"

echo "==== Linking dotfiles with stow... ===="
stow --adopt -v -t "$HOME" zsh ssh

echo "==== Restoring macOS preferences from prefs/*.plist... ===="
[ -f "$HOME/GitHub Repos/macOS/capture-macos" ] && bash "$HOME/GitHub Repos/macOS/capture-macos"

echo "==== Installing MAS apps (sign into App Store first for success)... ===="
mas install 441258766 || true # Magnet
mas install 1440147259 || true # AdGuard for Safari
mas install 1569813296 || true # 1Password for Safari
mas install 1544743900 || true # Hush

echo "==== Installing printer... ===="
sudo lpadmin -p "EPSON_ET_2760"              -E              -v "dnssd://EPSON%20ET-2760._ipps._tcp.local./?uuid=cfe92100-67c4-11d4-a45f-50579ca928af"              -m everywhere   && sudo cupsenable "EPSON_ET_2760"   && sudo cupsaccept "EPSON_ET_2760"   && lpoptions -d "EPSON_ET_2760"

echo "==== Manual checklist ===="
cat <<EOF
1) App Store: sign in if MAS installs said to wait.
2) 1Password: Settings -> Developer -> enable 'Use 1Password SSH agent'.
3) Safari -> Extensions: Enable 1Password, AdGuard, and Hush.
EOF

echo "==== Bootstrap complete. ===="

Automatic Backups

My .zshrc runs a daily backup routine:

if [ ! -f "$HOME/.last_backup" ] || [ $(($(date +%s) - $(cat "$HOME/.last_backup"))) -gt 86400 ]; then
  "$HOME/GitHub Repos/macOS/bin/backup-now"
  date +%s > "$HOME/.last_backup"
fi

The backup-now script:

  • Dumps a fresh Brewfile.
  • Copies dotfiles into the repo.
  • Commits and pushes to GitHub.

Conclusion

With this setup, I can unbox a new Mac, sign into iCloud and GitHub, run:

git clone https://github.com/zaclohrenz/macOS.git "$HOME/GitHub Repos/macOS"
cd "$HOME/GitHub Repos/macOS" && ./bootstrap.sh

…and in minutes have my full environment restored — apps, preferences, printer, and all.