How I set up a new Mac
November 17, 2021
November 17, 2021
Note: updated August 1, 2026 with Tahoe and more scripting.
As I recently blogged, I just upgraded to macOS Monterey taking it as an opportunity to start from a clean, fresh, pristine system. This means I had to set up everything again, which is not a big deal, but for the sake of remembering it and making it even faster next time, I figured Iād write about it.
This is the kind of blog post thatās mostly for my personal interest, but if you got there somehow, you might take inspiration from my settings, who knows!
But first, letās ask the following question.
While I could totally have migrated all my data from Big Sur, I like to start from a clean slate every year or two, to get rid of all the unnecessary garbage that accumulated over the years.
Wait, what garbage? Well, let me explain.
Every time you update a software (including the OS), thereās no guarantee that the state youāll be in after the update would be the same as if you installed the new version directly on a fresh system. Actually, the opposite is pretty much guaranteed.
Most of the time this is not a big deal. Maybe youāre stuck with the
default settings of the version you originally installed instead of
the ones that would otherwise come with the latest version (i.e. Git
always defaults to master and you have to explicitly configure it to
use main, or any small things like this).
Or maybe some commands or tasks might run slightly slower because of
accumulated ābloatā related to things you donāt use anymore and forgot
about (keys, passwords, certificates, trusted IP lists and whatnot
from stuff you connected to once or at least stopped connecting to
ages ago, the list of known Wi-Fi networks and ~/.ssh/known_hosts
being a typical example).
While the above are pretty inoffensive cases, this kind of undefined state drifts might cause more sneaky bugs, and āworks on that machineā kind of answers when you try to figure them out.
The same is also true when you uninstall a software; thereās no guarantee that the state youāll be in after the removal will match the one you would have been in if you didnāt install it in the first place. And again, the opposite is pretty much guaranteed.
NixOS solves some of those issues, but in the real world, youāre likely gonna want to use many programs that are not designed and packaged to be stateless, deterministic, reproducible and purely functional, and using wrappers (or wrapping them yourself) often comes at a tremendous cost in time and convenience.
My tradeoff so far? A fresh reinstall every other year, or whenever I feel like Iāve fucked around enough with that systemās state to be worth a clean start.
Hereās the things I do when I log in the first time on my freshly installed system.
Get rid of all the garbage in the dock. I just leave the Finder and the trash, because you canāt really remove them anyways (did you try dragging the trash to the trash?), but I happen to use them so thatās fine.
defaults write com.apple.dock persistent-apps -array
defaults write com.apple.dock persistent-others -array
killall Dock
Install Homebrew (only curl | sh Iāll tolerate).
I install it in the default place because otherwise it wonāt be able to
leverage many prebuilt binaries that hardcode the default prefix in
them, and itās utterly slow to compile everything. If you want to run
Homebrew on a multi-user system, read that first.
After the installation, Homebrew tells you to add eval "$(/opt/homebrew/bin/brew shellenv)" to your ~/.zprofile.
I personally prefer to use my ~/.zshenv for this, because itās sourced
all the time whereas .zprofile is sourced only for login shells.
Concretely this means that by setting the Homebrew environment
variables in .zshenv, I can do ssh me@my-machine brew ..., whereas
with .zprofile, I canāt.
Also I donāt like running eval "$(brew shellenv)" on every single
Zsh boot, Iād rather hardcode the output of brew shellenv in there
since itās not really supposed to change anyway:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
/opt/homebrew/bin/brew shellenv >> ~/.zshenv
brew install firefox iterm2 maccy cursor
# brew install firefox@developer-edition
# brew install google-chrome
(Maccy is a lovely super light clipboard history manager.)
Then install DaVinci Resolve and Logic Pro and download the full sound library. Sadly a fresh Logic installation canāt reuse an existing sound library directory (I like to keep mine on my hard drive instead of my limited size SSD), so we need to download the whole 60 GB from scratch.
I donāt like to clutter my desktop with screenshots like itās the case
by default, so I save them to ~/Desktop/Screenshots instead:
mkdir -p ~/Desktop/Screenshots
defaults write com.apple.screencapture location ~/Desktop/Screenshots
Now Iām ready to configure the system settings, iTerm2 preferences and my terminal environment.
# General > AirDrop & Continuity: Allow Handoff between this Mac and your iCloud devices
defaults -currentHost write com.apple.coreservices.useractivityd ActivityAdvertisingAllowed -bool false
defaults -currentHost write com.apple.coreservices.useractivityd ActivityReceivingAllowed -bool false
# General > Date & Time: 24-hour time
defaults write -g AppleICUForce24HourTime -bool true
# Accessibility > Zoom: Use scroll gesture with modifier keys to zoom (Control is default)
defaults write com.apple.universalaccess closeViewScrollWheelToggle -bool true
# Desktop & Dock > Dock: Automatically hide and show the Dock
defaults write com.apple.dock autohide -bool true
# Desktop & Dock > Dock: Don't show suggested and recent apps
defaults write com.apple.dock show-recents -bool false
# Desktop & Dock > Dock: Remove the delay to show and hide the dock
defaults write com.apple.dock autohide-delay -float 0
# Desktop & Dock > Windows: Tiled windows have margins
defaults write com.apple.WindowManager EnableTiledWindowMargins -bool false
# Desktop & Dock > Mission Control: Don't automatically rearrange Spaces
defaults write com.apple.dock mru-spaces -bool false
# Desktop & Dock > Hot Corners (disable all, bottom right is Quick Note by default)
defaults write com.apple.dock wvous-tl-corner -int 0
defaults write com.apple.dock wvous-tr-corner -int 0
defaults write com.apple.dock wvous-bl-corner -int 0
defaults write com.apple.dock wvous-br-corner -int 0
# Menu Bar: Spotlight (untick)
defaults -currentHost write com.apple.Spotlight MenuItemHidden -bool true
# Keyboard: Key repeat rate & Delay until repeat (fastest possible, I like a snappy keyboard)
defaults write -g KeyRepeat -int 2
defaults write -g InitialKeyRepeat -int 15
killall Dock
Note: log out and back in after running this block. Clock, keyboard and tiling settings donāt take effect immediately.
For Dock delay, see full blog post here.
Thanks to this blog post for disabling Continuity Handoff (prevent Dock icons from apps on other devices).
A few things I still need to do manually in system settings:
omw abbreviation. Iām
always staggered when I forget to do this and omw gets replaced by
On my way!, or when I press space twice and it inserts a colon
instead while I code in Cursor! š¤¦āāļøFor all Visual Studio Code based editors, I disable
ApplePressAndHoldEnabled for⦠sanity. And I also force new windows to open as tabs
(this can also be configured system-wide
but I only want it for those apps).
# Visual Studio Code
defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false
defaults write com.microsoft.VSCode AppleWindowTabbingMode -string always
# Cursor
defaults write com.todesktop.230313mzl4w4u92 ApplePressAndHoldEnabled -bool false
defaults write com.todesktop.230313mzl4w4u92 AppleWindowTabbingMode -string always
# Antigravity
defaults write com.google.antigravity ApplePressAndHoldEnabled -bool false
defaults write com.google.antigravity AppleWindowTabbingMode -string always
# Appearance > General > Theme: Minimal
defaults write com.googlecode.iterm2 TabStyleWithAutomaticOption -int 5
# Appearance > Windows: Hide scrollbars
defaults write com.googlecode.iterm2 HideScrollbar -bool true
# Appearance > Tabs: Preserve window size when tab bar shows or hides
defaults write com.googlecode.iterm2 PreserveWindowSizeWhenTabBarVisibilityChanges -bool true
# Appearance > Panes > Show per-pane title bars: off
defaults write com.googlecode.iterm2 ShowPaneTitles -bool false
# Appearance > Dimming > Dimming amount: 10
defaults write com.googlecode.iterm2 SplitPaneDimmingAmount -float 0.1
# Advanced > Mouse: Scroll wheel sends arrow keys when in alternate screen mode
defaults write com.googlecode.iterm2 AlternateMouseScroll -bool true
# Profiles > General > Initial directory: Reuse previous session's directory
/usr/libexec/PlistBuddy -c "Set ':New Bookmarks:0:Custom Directory' Recycle" ~/Library/Preferences/com.googlecode.iterm2.plist
# Profiles > Terminal > Bell: Silence bell
/usr/libexec/PlistBuddy -c "Set ':New Bookmarks:0:Silence Bell' true" ~/Library/Preferences/com.googlecode.iterm2.plist
Note: run this when iTerm2 is not running.
Finally (I couldnāt script this), in Profiles > Keys > Key Bindings, load the Natural Text Editing preset (allow it to remove whatever is already there), and remove Command + Left and Command + Right which otherwise shadow the shortcuts to navigate between tabs.
Starts with Xcode Command Line Tools to have git.
xcode-select --install
Then I make a SSH keypair or copy an existing one in ~/.ssh. I usually
run ssh-keygen either way just to let it create the directory with the
proper permissions, even if Iāll override the key later.
ssh-keygen -t ed25519
Clone my dotfiles directory and install my Mac preset (mainly my Zsh, Vim and Git settings).
git clone git@github.com:valeriangalliat/dotfiles.git
cd dotfiles
make mac
cd
Edit my default ~/.zshrc and ~/.zshenv templates and comment or
uncomment some of the stuff there that I may need, mainly enabling my
asdf helper (see below).
vim ~/.zshrc ~/.zshenv
Install whatever software I pretty much always use with Homebrew.
brew install rg imagemagick ffmpeg ncdu htop jq asdf wget tree watch moreutils yt-dlp
wget, tree, watch basic UNIX commands missing from macOS.sponge.Install the asdf plugins I need
and whatever version is in my ~/.tool-versions.
asdf plugin add nodejs
# asdf plugin add python
asdf install
Thatās pretty much the gist! This is a fairly straightforward and not very time consuming checklist, and the main things that need to be automated (my dotfiles) are.
Everything else is very specific to the current machine Iām setting up and I leave them to my discretion at the time of installing.
If you read until there, I hope that you learnt something, or that it inspired you to document your base setup in a similar way. Cheers!