Diagnosing Homebrew's Atomic Cellar Symlink Swap: Stale Versions and Orphaned Kegs
Diagnose why Homebrew installs succeed but leave old versions active. Covers Cellar symlink inspection, orphaned keg cleanup, filesystem atomic-rename constraints, and correct fix procedures with concrete nginx examples.
05 Oct 2026, 10:43 UTC

Recognizable Condition
You run brew upgrade nginx and the command exits with "Pouring nginx--1.26.1..." and "🍺 /opt/homebrew/Cellar/nginx/1.26.1: 24 files, 2.1MB", but nginx -v still reports 1.25.3. Running /opt/homebrew/Cellar/nginx/1.26.1/bin/nginx -v shows the new version. The binary in /opt/homebrew/bin/nginx (a symlink) points to the old keg. This mismatch means the Cellar active symlink — /opt/homebrew/Cellar/nginx — still points to 1.25.3 instead of 1.26.1.
Cause and Diagnostic Quick-Reference
| Symptom | Likely Cause | Quick Check |
|---|---|---|
brew upgrade succeeds but brew list --versions nginx shows old version | Cellar active symlink not updated | ls -la /opt/homebrew/Cellar/nginx — symlink points to old version directory |
Binary in /opt/homebrew/bin is stale | Prefix symlinks not recreated after Cellar swap | ls -la /opt/homebrew/bin/nginx — target shows old version path |
| Install interrupted (SIGKILL, power loss) | Stale temporary keg left in Cellar | ls -d /opt/homebrew/Cellar/nginx/*/ — extra directory like 1.26.1_1 or tmp.* not symlinked |
| Cellar on network mount or FAT/exFAT | Atomic rename not guaranteed on filesystem | df -T /opt/homebrew/Cellar — shows filesystem type (NFS, SMB, msdos, exfat) |
Ordered Diagnostic Checks
1. Inspect the Cellar Active Symlink
Run on the host where Homebrew is installed (read access to Cellar required):
ls -la /opt/homebrew/Cellar/nginx
Expected output on Apple Silicon (adjust /usr/local/Cellar for Intel macOS or Linux):
lrwxr-xr-x 1 user admin 6 Oct 10 14:22 /opt/homebrew/Cellar/nginx -> 1.26.1
If the symlink points to 1.25.3 or is missing, the atomic swap did not complete. The symlink is created by rename(2) from a temporary name after extraction and checksum verification.
2. Verify Homebrew's Install Record
brew info nginx
Look for the line starting with "From:" (bottle) or "Built from source on:" and the versioned path under "Cellar:". Example:
nginx: stable 1.26.1 (bottled)
From: https://ghcr.io/v2/homebrew/core/nginx/manifests/1.26.1
Cellar: /opt/homebrew/Cellar/nginx/1.26.1 (24 files, 2.1MB)
If the Cellar path shows 1.26.1 but the active symlink points to 1.25.3, extraction and verification succeeded but the final rename did not take effect.
3. Check for Orphaned Temporary Kegs
ls -d /opt/homebrew/Cellar/nginx/*/ | sort -V
Normal output shows only released version directories (e.g., 1.25.3/, 1.26.1/). Orphaned directories from interrupted installs often have suffixes like _1, _2 or prefixes like tmp.. These are not referenced by the active symlink.
4. Confirm Filesystem Type
df -T /opt/homebrew/Cellar
Output example on APFS:
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/disk3s1 apfs 488384512 312451072 175933440 64% /
APFS, ext4, xfs, and btrfs support atomic rename. NFS, SMB (cifs), msdos (FAT32), and exfat do not guarantee atomicity — a crash during rename can leave a broken or stale symlink. This check only identifies the filesystem; it does not prove atomic rename behavior.
Fixes Tied to Findings
Cellar Active Symlink Points to Wrong Version — Force the Swap
If the correct versioned directory exists (e.g., 1.26.1/) but the active symlink points to an older version:
brew unlink nginx && brew link nginx
This removes and recreates the prefix symlinks in /opt/homebrew/bin, /opt/homebrew/sbin, etc. It does not rewrite the Cellar active symlink. To fix the Cellar symlink directly, reinstall:
brew reinstall nginx
brew reinstall re-extracts the bottle (or rebuilds) and performs the atomic rename again. Requires write permission to /opt/homebrew/Cellar and /opt/homebrew.
Stale Temporary Keg Present — Clean Up
brew cleanup nginx
Removes unlinked versioned directories for that formula. If the orphaned directory is the only version present (active symlink missing entirely), brew cleanup will not remove it. In that case, delete the orphan manually and reinstall:
rm -rf /opt/homebrew/Cellar/nginx/1.26.1_1
brew reinstall nginx
Use the exact orphaned directory name from step 3. Do not use wildcards or empty placeholders.
Filesystem Lacks Atomic Rename — Relocate Before First Install
If df -T shows NFS, SMB, FAT32, or exFAT for the Cellar path, the atomic swap guarantee does not hold. Homebrew does not support changing HOMEBREW_CELLAR after packages are installed. To avoid this, set HOMEBREW_CELLAR to a local APFS/ext4 path before any Homebrew operation:
export HOMEBREW_CELLAR=/opt/homebrew/Cellar
brew install nginx
Add the export to your shell profile (~/.zprofile, ~/.bash_profile). Moving an existing Cellar requires migrating all kegs and updating the prefix symlinks — not covered here.
Permission Denied During Link Phase
If brew install or brew link reports "Permission denied" on /opt/homebrew/Cellar/nginx, the directory may be owned by root from a prior sudo brew run (unsupported). Fix ownership for the specific formula only:
sudo chown -R $(whoami) /opt/homebrew/Cellar/nginx
brew link nginx
Avoid recursive chown on the entire Cellar unless necessary; it can break multi-user setups or system-managed directories.
Escalation Criteria
Escalate to a full reinstall or Homebrew issue report when:
brew reinstall nginxfails with the same symlink mismatch.brew doctorreports "Unlinked kegs" or "Broken symlinks" thatbrew link nginxcannot resolve.- The Cellar lives on APFS or ext4 but
strace -f -e rename brew install nginx(Linux, requiresstraceinstalled) orsudo dtruss -f -t rename brew install nginx(macOS, requires SIP disabled and root) shows therenamesyscall failing or not being called. These tools require installation and elevated privileges; use only if earlier steps are inconclusive. - Multiple formulas exhibit the same symptom simultaneously — indicates a systemic Cellar or prefix permission problem.
Verification After Fix
Run all three checks to confirm resolution:
ls -la /opt/homebrew/Cellar/nginx— symlink points to latest version directory (e.g.,1.26.1).brew info nginx— Cellar path version matches the symlink target.nginx -v— binary in PATH reports the expected version (e.g.,nginx version: nginx/1.26.1).
If all three align, the atomic swap mechanism is functioning correctly.
Limitations
- This guide uses
/opt/homebrew/Cellar(Apple Silicon default). For Intel macOS, substitute/usr/local/Cellar; for Linux, typically/home/linuxbrew/.linuxbrew/Cellar. CustomHOMEBREW_CELLARorHOMEBREW_PREFIXpaths follow the same logic with path substitution. - Keg-only formulas (e.g.,
openssl@3,python@3.11) do not create prefix symlinks in/opt/homebrew/binby design. They only maintain the Cellar active symlink. Verify withbrew info --json=v2 nginx | jq '.formulae[0].keg_only'(returnsfalsefor nginx). - On macOS 13+ with SIP enabled,
/usr/localis not writable on Apple Silicon; Homebrew uses/opt/homebrew. The same atomic-swap logic applies. - Homebrew's internal temporary keg naming scheme may change between versions. The
_1,tmp.patterns are observed behaviors, not guaranteed APIs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.