My MacBook's internal storage was full. Not "almost full" — full. No more space for new projects, no room for dependencies, constant "Your disk is almost full" alerts.
I had a 1TB external SSD sitting on my desk doing nothing.
So I built a system to use the SSD as my Mac's primary storage — transparently, automatically, with a full terminal menu and 18 scheduled automation scripts running in the background.
Here's exactly how I did it, and how you can too.
The Problem
256GB internal SSD. Here's what was eating it:
| What | Size |
|---|---|
| Notion app data | 7.7G |
| Google Chrome cache | 7.4G |
| Claude desktop | 6.8G |
| OperaGX | 1.6G |
| VS Code | 1.7G |
| Discord | 998M |
| Whisky (unused) | 854M |
| Wondershare installer | 493M |
| TabNine models | 477M |
.rustup toolchain |
1.4G |
.android SDK |
515M |
.pub-cache |
434M |
.nvm Node versions |
204M |
.npm cache |
460M |
That's ~30GB in things that either don't need to be on the internal drive or can be moved transparently.
The Solution: SSD as Primary Storage via Symlinks
The core idea is simple. macOS follows symlinks transparently — apps and tools have no idea they're reading from an external drive.
# Move Downloads to SSD
mv ~/Downloads /Volumes/007/Downloads
ln -s /Volumes/007/Downloads ~/Downloads
# Now everything that writes to ~/Downloads
# actually writes to the SSD — no config changes needed
I did this for:
-
~/Downloads,~/Documents,~/Desktop,~/Movies - All Dev projects (
leadgen-platform,linkedin_automate, etc.) - Toolchains:
.rustup,.cargo,.android,.nvm,.npm,.m2,.gradle,.pub-cache
Result: 36GB freed on internal. Mac went from 96% full to 68% full in one session.
The Automation Layer
Moving files manually is a one-time fix. The real value is automation. I built 27 shell scripts and wired them to macOS launchd.
ssd — Terminal Menu for SSD Management
╔═══════════════════════════════════════════════════════════════════════╗
║ ◈ SSD TOOLS — 007 ◈ ║
╚═══════════════════════════════════════════════════════════════════════╝
● SSD 007 899G free / 33G used ● Mac Internal 36G free / 17G used
STORAGE & SYSTEM
┌──────┬──────────────────────┬────────────────────────────────────────┐
│ 1 │ mount-check │ Check SSD mount + symlink health │
│ 2 │ cache-cleanup │ Clear caches (Library, npm, pnpm) │
│ 3 │ low-disk-alert │ Check internal Mac free space │
│ 7 │ duplicate-finder │ Scan SSD for duplicate files │
│ 12 │ ssd-health │ SSD space + fill prediction │
└──────┴──────────────────────┴────────────────────────────────────────┘
DEV & CODE
┌──────┬──────────────────────┬────────────────────────────────────────┐
│ 5 │ dev-backup │ Rsync active projects to backup │
│ 9 │ git-status │ Uncommitted changes across projects │
│ 20 │ env-backup │ Backup all .env files │
│ 25 │ focus-mode │ Block distractions (25min) │
└──────┴──────────────────────┴────────────────────────────────────────┘
mac — Terminal Menu for Mac System Tools
╔═══════════════════════════════════════════════════════════════════════╗
║ ◈ MAC TOOLS — SHRAVAN ◈ ║
╚═══════════════════════════════════════════════════════════════════════╝
CPU 12% Battery 100% (charged) Uptime 2 days
Wifi HomeNetwork IP 192.168.1.6
SYSTEM APPS MAINTENANCE SECURITY POWER
system-stats kill-hogs clear-caches firewall-status caffeinate
battery-health app-usage brew-update open-ports sleep-timer
wifi-info startup-items run-all-agents lock-screen battery-saver
processes force-quit large-files ssh-keys
The 18 Scheduled LaunchAgents
These run automatically via macOS launchd — no cron, no manual triggers.
# Check what's running
launchctl list | grep shravan
| Agent | Schedule | What it does |
|---|---|---|
| dev-backup | Nightly 11pm | Rsync projects to SSD backup, keeps 7 days |
| dotfiles-backup | Nightly 10:30pm | Backup .zshrc, .gitconfig, SSH config |
| env-backup | Nightly 10pm | Backup all .env files, keeps 14 days |
| cache-cleanup | Monday 9am | Clear Library/Caches, npm, pnpm |
| git-status | Daily 9am | Report uncommitted changes across all projects |
| dep-audit | Monday 10am | npm vulnerability scan |
| ssd-health | Daily 10am | Space usage + fill rate prediction |
| low-disk-alert | Every hour | Alert if Mac internal < 5GB free |
| ram-monitor | Every 5min | Log RAM usage |
| battery-logger | Daily 12pm | Log battery cycles + health |
Serial Lock: SSD Only Works on My Mac
I didn't want the SSD to work if stolen. So I built a serial number lock:
#!/bin/zsh
AUTHORIZED_SERIAL="CPWY0XTG9Y"
CURRENT_SERIAL=$(system_profiler SPHardwareDataType | grep "Serial Number" | awk '{print $NF}')
if [ "$CURRENT_SERIAL" != "$AUTHORIZED_SERIAL" ]; then
diskutil eject disk5
osascript -e 'display alert "Unauthorized Mac. SSD ejected." as critical'
fi
This fires automatically via a WatchPaths LaunchAgent whenever the SSD mounts. Wrong Mac → instant eject.
The Install Script
The whole toolkit is packaged with a one-command installer:
git clone https://github.com/sxrxvxnn/mac-ssd-toolkit
cd mac-ssd-toolkit
./install.sh
It asks for your username, SSD volume name, and project names — then configures everything, installs LaunchAgents, and adds aliases to .zshrc.
Results
| Before | After |
|---|---|
| 23GB free | 36GB free |
| Manual backups | Nightly automated |
| No disk monitoring | Hourly alerts |
| Scattered dotfiles | Nightly backup |
| No SSD security | Serial lock |
What's Next
The repo is open source and looking for contributors. If you have ideas for new scripts — storage tools, dev workflow automation, system monitoring — open an issue or PR.
GitHub: github.com/sxrxvxnn/mac-ssd-toolkit
Ideas already in the backlog:
- Git commit streak tracker
- Wifi speed daily logger
- Auto-organize Downloads by file type
- Monthly storage trend report
If you're a Mac developer with limited internal storage, give it a try. One command and you're set up.
Built while working as a BD intern at Beagle Security, building Sonar — an Apollo-style B2B intelligence platform.
Top comments (1)
This is a very practical kind of automation because storage cleanup is usually death by small forgotten directories. Scripts help most when they make the state visible before deleting or moving anything.
I would keep a dry-run mode and a recovery path as first-class features. External SSD workflows are great, but the dangerous moment is when a script turns "move cache" and "move source of truth" into the same-looking operation.