MacOS Sonoma Breaks Symlinks

TL;DR

If you're using symlinks in MacOS Sonoma to manage dotfiles (especially with something like mackup), it will not work on MacOS 14.


Like many developers, I keep a repository consisting of dotfiles, scripts, and other configuration settings that I need to make a computer feel like "mine".

I also keep a list of .plist files for defining native MacOS keyboard shortcuts, global preferences such as how Finder displays files, and even app-specific configuration settings!

Almost all MacOS apps have a .plist file which stores your preferences for that specific app. This means if you adjust your settings, you can save that .plist file and then have the app behave exactly the same on a different MacOS computer!

This is ideal for keeping keyboard shortcuts and app defaults consistent across multiple computers.

But something broke

One day I had to completely reset my work laptop, which meant I needed to re-initialize my dotfiles repository.

In this repository I have a script that operates like so:

# Install all the configured preferences for our various apps
ln -sfn ${DOTFILES_LOCATION}/prefs/*.plist "${HOME}/Library/Preferences"
ln -sfn ${DOTFILES_LOCATION}/prefs/.GlobalPreferences.plist "${HOME}/Library/Preferences"

This bit of logic copies all of my .plist files and MacOS global preferences into the Library/Preferences directory.

However, it doesn't just copy those files over. It's creating a symlink.

This behavior is extremely useful so that your dotfiles can easily back up your configuration changes! If you update your app's preferences, your dotfiles will see the change and let you know you should commit them.

But MacOS Sonoma broke symlinks

In several different places you can see tickets and issues caused by this change. My best guess is that the discovery of a CVE related to symlinking caused Apple to change this behavior. (Here are a couple other supporting links too).

At any rate, it's very strange, because as one GitHub user astutely pointed out:

[...] surprising, given that symlinks should be transparent to most file reads

My understanding was that most applications aren't really aware of whether or not they're accessing a symlink, which isn't to say they can't be aware, but that they usually aren't.

My solution was pretty dang simple: just copy the config files over instead of symlinking them.

That's it.

Yeah, it's not as elegant, but at least my dotfiles still work:

cp -R ${DOTFILES_LOCATION}/prefs/*.plist "${HOME}/Library/Preferences"
cp -R ${DOTFILES_LOCATION}/prefs/.GlobalPreferences.plist "${HOME}/Library/Preferences"

And just to sprinkle a bit of SEO Sauce on this post.

When you run this:

defaults read NSGlobalDomain

You'll see something like this:

Domain Apple Global Domain does not exist

Despite being able to see it right there in the file system:

ls /User/youruser/Library/Preferences | grep NSGlobal

Other symptoms of this issue include things like: