Skip to content

One Mac, two users, one Homebrew

8 min read
#macos#homebrew#docker#dotfiles#setup

At some point you end up wanting two accounts on one Mac. Maybe the work you’re doing should live somewhere your personal browser sessions and SSH keys don’t. Maybe your employer wants a managed account and you’d rather not hand over the whole machine. Whatever the reason, the request is modest: one laptop, two logins, a clean line between them.

macOS handles the first ninety percent of that well. Separate home directories, separate keychains, separate browser profiles, fast user switching. Then you open a terminal in the new account and type brew, and nothing happens.

What follows is everything that broke, and why each thing broke, because the reasons turn out to be more interesting than the fixes.

The core problem: two different ideas of ownership

macOS is built around per-user isolation. Almost everything meaningful lives under ~/Library, and a second account genuinely starts from nothing.

Homebrew is built around a single shared prefix. On Apple Silicon that’s /opt/homebrew, and it is owned by whichever user installed it. Not by root, not by a group, but by a person.

Those two models are individually sensible and jointly awkward. Your second account can see /opt/homebrew because it’s world-readable, but it can’t write to it, and Homebrew wants to write constantly: updating formulae, linking binaries, managing its own git repository.

Homebrew ownership across two macOS accountsThe personal account owns and writes to /opt/homebrew. The work account cannot write to it, but can read and execute everything in /opt/homebrew/bin. The Cellar, git repository and formulae inside the prefix remain reachable only by the owning account.akcpersonalworksecond accountowns · writesreads · executes/opt/homebrewbin/world-readable · world-executableCellar/ · .git/ · formulae/akc only
Management needs write access; the binaries it produces don't. That gap is what lets one Homebrew serve both accounts.

There are three ways out and only one of them is good.

Install a second Homebrew. Works, and it’s the option Homebrew’s own documentation is most comfortable with. But you now have two package databases, two update cycles, and several gigabytes of duplicated Xcode-adjacent dependencies on a laptop SSD. You will also, eventually, be confused about which one you’re talking to.

Change ownership to a shared group. Tempting, and it appears in a lot of forum answers. It’s fragile. Homebrew resets permissions during some operations, and you end up re-running chown after upgrades and wondering why things broke.

Run brew as the user who owns it. This is the one I settled on:

alias brew='sudo -Hiu akc brew'

Where akc is my personal account, the one that installed Homebrew. The flags matter:

  • -u akc runs as that user
  • -i starts a login shell, so Homebrew gets the environment it expects rather than an inherited one from the calling account
  • -H sets $HOME to the target user’s home, which is what stops Homebrew writing its cache and logs into the wrong place

The result is that installing a package from the work account installs it into the personal account’s Homebrew, and both accounts use it.

That last part is the piece worth understanding. Homebrew’s management needs write access, but the things it installs land in /opt/homebrew/bin, which is readable and executable by everyone on the machine. So the second account doesn’t need Homebrew at all in order to use what Homebrew installed. It just needs the path:

# ~/.zprofile
export PATH="$PATH:/opt/homebrew/bin"

.zprofile rather than .zshrc, because this is environment setup that should happen once at login rather than on every interactive shell.

Docker, which fails differently

Docker Desktop breaks for a related but distinct reason, and the failure looks identical from the terminal: docker: command not found.

The application itself is in /Applications and every account can launch it. But Docker Desktop installs its CLI tools (docker, docker-compose, docker-buildx) into the user’s home directory at ~/.docker/bin, and then symlinks or path-injects them per account. The second user gets the app and none of the commands.

# ~/.zprofile
export PATH="$PATH:$HOME/.docker/bin"

Launch Docker Desktop once from the new account first, so it populates that directory. There’s a long-running thread on docker/for-mac about multi-user behaviour if you want the full history.

The obvious move is to copy your config across. Don’t. You’ll edit one, forget the other, and spend a month wondering why your Neovim keybindings differ depending on which account you’re in.

Symlink instead, so there’s one source of truth. There are two places that source of truth can live, and the difference matters more than it first appears.

Option one: keep the repo in one user’s home and link across.

cd ~/.config
ln -s /Users/akc/.dotfiles/nvim/.config/nvim/ nvim
ln -s /Users/akc/.dotfiles/lazygit/.config/lazygit/ lazygit

This works, and it’s the smaller change if your dotfiles already live in ~. One caveat to check on your own machine: macOS makes a home directory itself readable by other users but locks down several subdirectories inside it. A .dotfiles repository sitting directly in ~ is reachable; the same repo inside ~/Documents is not. If your symlinks resolve but the target won’t open, that’s the reason, and moving the repo up a level fixes it.

Option two: move the repo to /Users/Shared and link from both accounts.

mv ~/.dotfiles /Users/Shared/dotfiles

cd ~/.config
ln -s /Users/Shared/dotfiles/nvim/.config/nvim/ nvim
ln -s /Users/Shared/dotfiles/lazygit/.config/lazygit/ lazygit

/Users/Shared exists on every Mac for exactly this: a directory outside anyone’s home that all local accounts can read and write. This is what I ran for a long time, and it’s the better default.

Two reasons. The permissions problem disappears, because you’re no longer reaching into another account’s home directory and hoping macOS lets you. And it removes the asymmetry. With option one there’s a primary account that owns the config and a secondary one that borrows it, so deleting or renaming that account takes your dotfiles with it. In /Users/Shared neither account is privileged, and both are just clients of the same repository.

The tradeoff is that /Users/Shared is writable by every local account, so anything with a credential in it doesn’t belong there. Config yes, secrets no. That’s the rule anywhere, but it’s easier to violate by accident when the directory is this convenient.

Shell config is the exception. .zshrc, .zprofile and .zshenv genuinely differ between the accounts, with different aliases, different paths and different work-specific variables, so those get copied once rather than linked, and then diverge on purpose.

Moving files between your own two accounts

This is more annoying than it should be, because from the filesystem’s point of view you are two unrelated people.

Every macOS account has a ~/Public folder that is readable by other users on the machine, with a Drop Box inside it that is write-only from outside. That’s the intended channel, and it’s what I use for the one-time transfers: shell config, zsh plugin directories (zsh-autosuggestions, fast-syntax-highlighting), fonts.

Fonts are worth a note of their own. Installing to ~/Library/Fonts makes them available to one account only; /Library/Fonts makes them available to everyone. If you’re using a patched Nerd Font in your terminal, install it system-wide once and stop thinking about it.

For iTerm2, don’t hand-recreate your setup. Export the profile as JSON from the original account, drop it in ~/Public, and import it in the new one.

What the setup actually looks like

After all of it, the division is:

Shared

  • Homebrew and everything it installs
  • System fonts
  • Docker Desktop, the application
  • A dotfiles repo in /Users/Shared, symlinked into ~/.config from both

Separate

  • Shell configuration
  • SSH keys
  • Git identity
  • Keychains and browser profiles
  • Application state
  • Docker's per-user CLI and contexts

Which is roughly the right line. The things that are expensive to duplicate are shared; the things that carry identity or credentials are not.

The thing I keep thinking about

None of these are bugs. Homebrew’s single-prefix design is a deliberate trade that makes the common case fast and simple, and macOS’s per-user isolation is correct. Docker Desktop putting its CLI in the user’s home is defensible too.

But every one of them encodes an assumption that a laptop belongs to one person, and that assumption is doing a lot of quiet work. The moment you need two accounts, whether for a job, a contract, or a boundary you want to keep, you’re not fighting a limitation so much as discovering a premise nobody wrote down.

There’s a version of this in most software I work on. The default configuration is the shape of someone’s assumption about who you are, and you only find out what that assumption was when you stop matching it.

Read next