MacBook M4 2025 Migration Log
Summary
This document chronicles the migration from an Intel x86_64 MacBook to an Apple Silicon M4 MacBook (arm64) using macOS Migration Assistant. The migration revealed several architecture-dependent issues and provided valuable insights for improving the dotfiles bootstrap process.
Key Statistics:
- Migration time: ~1 hour
- Architecture: Intel x86_64 → Apple Silicon arm64
- Major issues: 7 (PAM/sudo, bootstrap script, broken symlinks, architecture mismatches, Homebrew dual-install, Docker, cache incompatibility)
- Final state: 100% native arm64 applications, zero Rosetta 2 dependencies, ~121MB of x86_64 files removed
Phase 1: Pre-Migration & Initial Setup
Migration Assistant Transfer
- Used macOS Migration Assistant to transfer all data from Intel MacBook
- Duration: Approximately 1 hour
- A few non-essential documents failed to transfer
- Migration Assistant prompted to update the source laptop to avoid issues
- Problem: 2020 Intel Mac was not supported for macOS 26 update
- Proceeded anyway without apparent consequences
Initial System Configuration
After first boot on the new MacBook:
Apple Wallet Cards
- System prompted to add cards from Apple Wallet
- Issue: None of the cards were able to be verified
- Status: Unresolved in this session
Application Permissions Required manual permission grants for:
- BetterTouchTool (Accessibility, Screen Recording)
- Logi Options++ (Accessibility)
- Karabiner (Observer/Grader Input permissions)
Account Setup
- Added Google account as Internet Account for email
- Bitwarden app and credentials transferred seamlessly
- ChatGPT extension for Siri required re-enabling and sign-in
- Quick Look extensions (Windows and Xcode) required manual enabling
Phase 2: Bootstrap Script Issues
Issue 1: PAM Initialization Error
Attempted to run the dotfiles startup script, which started Nix installation.
Error:
sudo: unable to initialize PAM: Undefined error: 0
Root Cause:
The Intel Mac had a custom PAM configuration at /etc/pam.d/sudo that included:
auth include sudo_local
auth sufficient pam_tid.so
auth sufficient pam_smartcard.so
auth sufficient pam_smartcard.so
auth required pam_opendirectory.so
account required pam_permit.so
password required pam_deny.so
session required pam_permit.so
The sudo_local file referenced pam_reattach.so which was compiled for x86_64 and incompatible with arm64.
Context:
The PAM modification was needed to enable Touch ID with tmux in the terminal. Tmux persists across user login sessions, which breaks the default PAM integration. The pam_reattach.so module fixes this by reattaching to the user login session scope.
Resolution:
- Used macOS Finder trick to gain write permissions to
/etc/pam.d/sudo:open /etc/pam.d/in terminal- Selected sudo file in Finder
- Unlocked details and granted read/write access temporarily
- Removed
sudo_localinclude from/etc/pam.d/sudo - Immediately restored read-only permissions for everyone
sudofunctionality restored
Issue 2: Pre-existing Backup Files
Error:
---- oh no! --------------------------------------------------------------------
I back up shell profile/rc scripts before I add Nix to them.
I need to back up /etc/bash.bashrc to /etc/bash.bashrc.backup-before-nix,
but the latter already exists.
Root Cause: Migration Assistant transferred old Nix backup files from the Intel Mac.
Additional Discovery:
The /etc/bash.bashrc file already contained Nix daemon initialization code (also migrated from Intel Mac).
Resolution:
sudo rm /etc/bash.bashrc.backup-before-nix
sudo rm /etc/zshrc.backup-before-nixThen manually cleaned the Nix daemon shell script from /etc/bash.bashrc and recreated a clean backup file to represent the state before Nix installation.
Issue 3: Broken Symlinks
Error:
/dev/fd/11: line 61: /Users/achhina/.config/nix/nix.conf: No such file or directory
Root Cause: Migration Assistant copied Home Manager symlinks that pointed to Nix store paths from the Intel Mac:
nix.conf: broken symbolic link to /nix/store/mihd906bd8vlm09q3w6apak7gxq0n9h3-home-manager-files/.config/nix/nix.conf
Resolution:
rm ~/.config/nix/nix.confIssue 4: Bootstrap Script Bug
Error:
Checking for conflicts with existing /Users/achhina/.config...
/dev/fd/11: line 112: conflicts[*]: unbound variable
Root Cause: Bootstrap script attempts to expand array variable without checking if it’s set.
Status: Needs fix (see Phase 2 action items)
Phase 3: Manual Dotfiles Installation
At this point, the bootstrap script had too many issues. Proceeded with manual installation.
Repository Setup
Discovery: The .git directory was already present (transferred via Migration Assistant), so no need to reinitialize.
Issue: Lost .git directory at some point during troubleshooting (likely during mv ~/.config ~/.config.backup operations).
Resolution:
git init
git remote add origin https://github.com/achhina/dotfiles.git
git fetch --all
git reset --hard origin/mainHome Manager Installation Attempt
Initial Error:
nix run home-manager/master -- switch --flake ~/.config/nix#$(scutil --get LocalHostName)
# Output: MacBook-Pro
error: flake 'path:/Users/achhina/.config/nix' does not provide attribute
'packages.aarch64-darwin.homeConfigurations."MacBook-Pro".activationPackage'Root Cause:
The flake defined homeConfigurations.achhina and homeConfigurations.default, but scutil --get LocalHostName returned "MacBook-Pro", which didn’t match any defined configuration.
Attempted Fix:
nix run home-manager/master -- switch --flake ~/.config/nix#achhinaIssue 5: Architecture Mismatch in Flake
Error:
error: Cannot build '/nix/store/aw8s03lz3ficjlh2cn998a0r8vhdh3rr--.drv'.
Reason: required system or feature not available
Required system: 'x86_64-darwin' with features {}
Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test}
This error appeared for multiple packages:
- bordersrc.drv
- builder.pl.drv
- detex-70015.drv
- hm-modules-messages.drv
- hm-session-vars.sh.drv
- org.nix-community.home.aerospace.plist.drv
- activation-script.drv
- home-manager-generation.drv
Root Cause:
The flake was using builtins.currentSystem which doesn’t work correctly in flake context. The configuration was hardcoded for x86_64-darwin.
Resolution:
Updated flake.nix to explicitly specify the architecture:
homeConfigurations."MacBook-Pro" = mkHomeConfiguration "aarch64-darwin";Home Manager Activation
File Collision Error:
Existing file '/Users/achhina/.config/nix/nix.conf' would be clobbered
Resolution:
nix run home-manager/master -- switch --flake ~/.config/nix#$(scutil --get LocalHostName) -b backupSuccess: Home Manager activated successfully with backup of existing files.
Post-Activation: Required re-granting Accessibility permissions to:
- Aerospace
- Borders (jankyborders)
- BetterTouchTool
- Logi Options++
Issue 6: Untrusted User Warnings
Warnings during activation:
warning: ignoring untrusted substituter 'https://nix-community.cachix.org', you are not a trusted user.
warning: ignoring untrusted substituter 'https://cache.garnix.io', you are not a trusted user.
warning: ignoring the client-specified setting 'trusted-public-keys', because it is a restricted setting and you are not a trusted user
Resolution:
# Add to /etc/nix/nix.conf
echo "trusted-users = root @admin" | sudo tee -a /etc/nix/nix.conf
# Restart Nix daemon
sudo launchctl stop org.nixos.nix-daemon
sudo launchctl start org.nixos.nix-daemonPhase 4: Architecture Migration Issues
Issue 7: Cached Binaries Architecture Mismatch
Symptoms:
- Neovim plugins failing to load (fzf)
- Pre-commit gitleaks hook failing
- Various executable incompatibilities
Root Cause:
Migration Assistant transferred the ~/.cache directory containing x86_64 compiled binaries.
Resolution:
rm -rf ~/.cacheCache directories are typically regenerated on demand.
Neovim Plugin Reinstallation
After removing cache, had to reinstall all Neovim plugins:
Issue with Lazy.nvim:
- Visual highlighting all packages and pressing
Xdidn’t work - Had to visual highlight, press
x, thenIto install all
Single Plugin Failure:
markdown-preview.nvim markdown md MarkdownPreviewToggle MarkdownPreview MarkdownPreviewStop
Vim:E117: Unknown function: mkdp#util#install
Status: Plugin partially broken, needs investigation
System Settings
Keyboard Remapping:
- Manually mapped Caps Lock → Control
- Location: System Settings → Keyboard → Keyboard Shortcuts
Phase 5: Docker & Container Setup
Docker Desktop Issues
Problem 1: Docker Desktop (GUI) had CPU architecture mismatch (x86_64)
Resolution:
- Removed Intel Docker Desktop
- Installed Docker engine via Nix
- Installed Docker Desktop (arm64) from official website
- Note: Could also be installed via Homebrew
Problem 2: Container images showed amd64 emulation warnings about potential slowdowns
Resolution:
# Pull arm64 images
docker pull --platform linux/arm64 [image-name]
# Set default builder to arm64
# Note: This required Docker Desktop restartProblem 3: Default builder communication error
Resolution:
- Enabled default socket communication in Docker Desktop Advanced settings
- Verified
/var/run/docker.socksymlink points to default builder
Phase 6: PAM/Touch ID Final Fix
Complete PAM Resolution
Background:
Earlier fix temporarily disabled pam_reattach to restore sudo. Now needed to restore Touch ID functionality with arm64-compatible module.
Problem:
Original pam_reattach.so was x86_64 (Intel Homebrew at /usr/local).
Resolution:
- Install Apple Silicon Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Installed to /opt/homebrew (arm64 location)
- Install arm64 pam-reattach:
/opt/homebrew/bin/brew install pam-reattach- Update PAM configuration:
Updated
/etc/pam.d/sudo_localto point to:
/opt/homebrew/lib/pam/pam_reattach.so
- Configured Homebrew PATH in Nix:
Added to
~/.config/nix/home-manager/modules/shell.nix:
programs.zsh.profileExtra = ''
eval "$(/opt/homebrew/bin/brew shellenv)"
'';- Applied changes:
hm switchResult: Touch ID working with sudo in tmux sessions
Phase 7: Complete Intel → Apple Silicon Migration
Homebrew Dual Installation Cleanup
Discovered State:
- Intel Homebrew at
/usr/local/bin/brew(x86_64) - Apple Silicon Homebrew at
/opt/homebrew/bin/brew(arm64)
Package Migration (5 packages)
Migrated from Intel to Apple Silicon Homebrew:
pam-reattach(formula)firefoxpwa(formula)blackhole-2ch(cask)logi-options+(cask)sublime-text(cask)
Intel Homebrew Complete Removal
Cleanup process:
# Remove all packages from Intel Homebrew
/usr/local/bin/brew uninstall --force --ignore-dependencies [package]
# Remove Intel Homebrew core
sudo rm -rf /usr/local/Homebrew
# Clean up orphaned configs and data (~25MB)
sudo rm -rf /usr/local/etc
sudo rm -rf /usr/local/share
sudo rm -rf /usr/local/var
# Remove Intel-only drivers (3 copies of chromedriver, 48MB)
sudo rm -rf /usr/local/Caskroom
# Remove osxfuse and Lua modules (x86_64)Final Verification:
find /usr/local -name "*x86_64*" 2>/dev/null | wc -l
# Output: 0Result: Zero Intel Homebrew remnants
System-Wide Application Updates (9 apps)
Migrated applications from Intel to arm64 native:
- Spotify - Intel → arm64 via Homebrew
- VLC - Intel → arm64 v3.0.21 via Homebrew
- Inkscape - Intel → arm64 v1.4 via Homebrew
- OBS Studio - Intel → arm64 v32.0.1 via Homebrew
- Anki - Intel → Universal binary via Homebrew
- Google Drive - Intel → arm64 v116.0.4 via Homebrew
- JupyterLab Desktop - Intel → arm64 v4.2.5 via Homebrew
- Streamlabs Desktop - Intel → arm64 v1.19.6 via Homebrew
- Obsidian - Migrated from Homebrew to Nix (cross-platform)
Applications Removed
No arm64 support or outdated:
- VirtualBox - No Apple Silicon support (alternatives: UTM, Parallels)
- LimeChat - Old Intel IRC client (alternatives: WeeChat, Srain, web IRC)
- Fliqlo screensaver - Intel-only
- Old Python 3.8 modules - ~1,450 x86_64 files
Nix Configuration Improvements
packages.nix reorganization:
Before:
darwinPackages = [ aerospace iterm2 jankyborders keycastr obsidian ];After:
# Cross-platform GUI applications
guiApps = [ obsidian ];
# macOS-specific packages
darwinPackages = [ aerospace iterm2 jankyborders keycastr ];Key Finding: OBS Studio is NOT available in nixpkgs for macOS (Linux-only in Nix), so it correctly remains in Homebrew.
Files Modified:
/Users/achhina/.config/nix/home-manager/modules/packages.nix- Package reorganization/Users/achhina/.config/nix/home-manager/modules/shell.nix- Homebrew PATH initialization/etc/pam.d/sudo_local- arm64 pam_reattach path
Final State & Statistics
Before Migration
- 10 Intel-only apps running via Rosetta 2
- ~2,700 x86_64 binaries across system
- Dual Homebrew installation (Intel + Apple Silicon)
- sudo broken due to PAM architecture mismatch
After Migration
- 0 Intel-only apps
- 100% native arm64 applications
- Single Apple Silicon Homebrew installation
- sudo working perfectly with Touch ID
- ~121MB of x86_64 files removed
Package Management Strategy
Declarative (Nix): CLI tools, dev tools, cross-platform apps (Obsidian)
Homebrew: macOS-specific apps not available or broken in Nix (OBS, JupyterLab Desktop, firefoxpwa, blackhole-2ch audio driver, pam-reattach, Spotify, etc.)
Performance Impact
- No more Rosetta 2 translation overhead
- Better battery life (native execution uses less power)
- Faster app launch times
- Lower memory usage
- Improved thermal efficiency
Known Issues & Action Items
Bootstrap Script Needs Fixes
- [FIXED] Unbound variable error at line 112 (
conflicts[*]) - [FIXED] No broken symlink detection or cleanup
- [FIXED] No architecture detection/warning
- [FIXED] No pre-existing Nix installation backup handling
Documentation Gaps
- [FIXED] No pre-migration checklist (what to remove/clean before migration)
- [FIXED] No architecture migration guide
- [FIXED] Hostname vs username configuration strategy not documented
- [FIXED] Trusted-users Nix requirement not documented
Configuration Cleanup
- [NOT NEEDED] Legacy “default” configuration still in flake.nix (already clean)
- [DOCUMENTED] markdown-preview.nvim plugin partially broken
- Edge case during architecture migration when VimScript autoload not ready
- Workaround documented in docs/troubleshooting.md
- Cannot be systematically prevented (race condition during lazy.nvim rebuild)
Unresolved Issues
- [UNRESOLVED] Apple Wallet cards unable to be verified
Follow-up 2026-08-08: the “100% native” claim was scoped too narrowly
Intel leftovers were still on the machine more than three months after this project closed. Found and removed during an unrelated cleanup (2026-08-08 Dead Intel Kexts and Flash Remnants).
What survived:
| Path | Detail |
|---|---|
/Library/Extensions/intelhaxm.kext (+ .zip) | x86_64, unloaded |
/Library/Extensions/HighPointRR.kext | x86_64, unloaded |
/Library/Extensions/HighPointIOP.kext | x86_64, unloaded |
/Library/Extensions/SoftRAID.kext | x86_64, unloaded |
/Library/LaunchDaemons/com.intel.haxm.plist | pointed into the HAXM kext |
/Library/LaunchDaemons/com.adobe.fpsaud.plist | Adobe Flash updater, EOL 2020-12-31 |
/Library/Application Support/Adobe, .../Macromedia | Flash remnants |
All four kexts were Mach-O 64-bit x86_64 kext bundle on an arm64 machine. /Library/Extensions is now empty.
Why the original verification missed them. The completion check was find /usr/local -name "*x86_64*", which only covers the Intel Homebrew prefix. /Library/Extensions and /Library/LaunchDaemons were never in scope. Nothing surfaced them at runtime either, because a wrong-architecture kext does not fail loudly, it simply never loads. So “0 Intel-only apps” was true of applications and Homebrew, and silently untrue of system components. The 2026-07-13 The Intel Audit That Could Not See Shims pass missed them for a different reason: it was hunting runtime shims, not unloadable system extensions.
Correction to Final State above: read “100% native arm64 applications” as scoped to applications and Homebrew packages as of 2026-04-27. System-level kexts and LaunchDaemons were not audited until 2026-08-08.
Generalizable lesson: an architecture migration needs a verification that enumerates by architecture across the filesystem, not by absence within one package manager’s prefix. The useful probe is file on the actual binaries in /Library/Extensions, /Library/LaunchDaemons targets, /Library/Application Support, and login items via sfltool dumpbtm, rather than a name-pattern search under a single prefix.
Backup of everything removed: ~/Downloads/dead-intel-leftovers-2026-08-08.tar.gz.
Second batch, same day. Widening the audit past kexts found six more, about 63M:
| Path | Size | Arch |
|---|---|---|
/Library/Internet Plug-Ins/PepperFlashPlayer | 50M | Flash, EOL 2020 |
/Library/Audio/Plug-Ins/HAL/LogiGamingAudio.driver | 11M | x86_64 |
/Library/PreferencePanes/Flash Player.prefPane | 912K | x86_64 |
/Library/Frameworks/OSXFUSE.framework | 456K | ppc7400, ppc64, i386, x86_64 |
/Library/Audio/Plug-Ins/HAL/MSTeamsAudioDevice.driver | 380K | x86_64, Teams absent |
/Library/PrivilegedHelperTools/com.microsoft.office.licensingV2.helper | small | no Office installed |
OSXFUSE.framework still carried PowerPC slices, so it predated even the Intel machine. Phase 7 above records removing “osxfuse and Lua modules (x86_64)”, but only the Lua side and the kext went; the framework survived. /Library/Internet Plug-Ins and /Library/PreferencePanes are now empty. Backup: ~/Downloads/dead-leftovers-batch2-2026-08-08.tar.gz.
Verified clean and left alone: all 45 bundles in /Applications are arm64 (that half of the original claim holds), ParrotAudioPlugin.driver is arm64e, and the Docker, Zoom, and Microsoft AutoUpdate privileged helpers all have live owners.
Method note for any future migration. Both batches were found by enumerating architecture, not by searching for names. lipo -archs on the real binary under /Library/Extensions, /Library/Internet Plug-Ins, /Library/PreferencePanes, /Library/Frameworks, /Library/Audio/Plug-Ins/HAL, and each LaunchDaemon’s program path, cross-checked against whether the owning application still exists. Name-pattern searches under a single prefix cannot find any of this.
Confirmed still working from this project: the Phase 6 PAM fix. /etc/pam.d/sudo_local still points at /opt/homebrew/lib/pam/pam_reattach.so, and TouchID sudo authenticated the removal.