If you use version managers to juggle multiple programming language runtimes on macOS, you're probably sitting on 5-30GB+ of old, unused versions right now. Each tool keeps every version you've ever installed, and none of them automatically clean up after themselves.
This guide covers every major version manager on macOS: where each stores its data, how to audit disk usage, and the exact commands to clean up safely.
Run this comprehensive audit to see how much disk space all version managers are consuming:
# === Version Manager Disk Audit ===
echo "=== Node.js version managers ==="
du -sh ~/.nvm/ 2>/dev/null
du -sh ~/Library/Caches/fnm_multishells/ 2>/dev/null
du -sh ~/.fnm/ 2>/dev/null
du -sh ~/.volta/ 2>/dev/null
echo ""
echo "=== Python version managers ==="
du -sh ~/.pyenv/ 2>/dev/null
echo ""
echo "=== Ruby version managers ==="
du -sh ~/.rbenv/ 2>/dev/null
du -sh ~/.rvm/ 2>/dev/null
echo ""
echo "=== JVM version managers ==="
du -sh ~/.sdkman/ 2>/dev/null
du -sh ~/.jabba/ 2>/dev/null
echo ""
echo "=== Universal version managers ==="
du -sh ~/.asdf/ 2>/dev/null
du -sh ~/.local/share/mise/ 2>/dev/null
echo ""
echo "=== Rust ==="
du -sh ~/.rustup/ 2>/dev/null
echo ""
echo "=== Go ==="
du -sh ~/.goenv/ 2>/dev/null
du -sh ~/sdk/ 2>/dev/null
Typical results on a multi-language developer Mac:
| Version Manager | Directory | Typical Size |
|---|---|---|
| nvm | ~/.nvm/ | 2-10GB |
| fnm | ~/.fnm/ | 1-5GB |
| Volta | ~/.volta/ | 1-5GB |
| pyenv | ~/.pyenv/ | 2-8GB |
| rbenv | ~/.rbenv/ | 1-5GB |
| SDKMAN | ~/.sdkman/ | 3-15GB |
| asdf | ~/.asdf/ | 2-10GB |
| rustup | ~/.rustup/ | 2-8GB |
nvm stores a complete Node.js installation for every version you've ever used. With Node releases happening frequently, it's easy to accumulate 5-10+ versions.
# See total nvm disk usage du -sh ~/.nvm/ 2>/dev/null # Size of each installed version du -sh ~/.nvm/versions/node/*/ 2>/dev/null | sort -hr # Count installed versions nvm ls | grep -c "v[0-9]" # See which versions are in use nvm ls nvm current
# Uninstall specific old versions nvm uninstall 16.20.2 nvm uninstall 18.17.0 # Keep only the latest LTS and current # First, check what you have: nvm ls # Install latest LTS if not current nvm install --lts nvm alias default lts/* # Remove all old versions (keep latest LTS) for v in $(nvm ls --no-colors | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | sort -V | head -n -1); do echo "Removing $v..." nvm uninstall "$v" done # Also clean npm global cache for each version npm cache clean --force
fnm is faster than nvm but has the same disk usage pattern. It also creates multishell directories that accumulate:
# fnm disk usage du -sh ~/.fnm/ 2>/dev/null du -sh ~/Library/Caches/fnm_multishells/ 2>/dev/null # List installed versions fnm ls # Size per version du -sh ~/.fnm/node-versions/*/ 2>/dev/null | sort -hr
# Uninstall specific versions fnm uninstall 18.17.0 fnm uninstall 16.20.2 # Clean multishell cache (safe to delete) rm -rf ~/Library/Caches/fnm_multishells/ 2>/dev/null # List and remove all except current current=$(fnm current) for v in $(fnm ls | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | grep -v "$current"); do echo "Removing $v" fnm uninstall "$v" done
~/Library/Caches/fnm_multishells/ can grow to hundreds of directories. Users have reported 2,398+ directories accumulating over time. It's safe to delete this directory.
Volta manages Node, npm, yarn, and other JS tools. It caches all downloaded archives:
# Volta disk usage du -sh ~/.volta/ 2>/dev/null # Breakdown by type du -sh ~/.volta/tools/ 2>/dev/null du -sh ~/.volta/cache/ 2>/dev/null du -sh ~/.volta/tmp/ 2>/dev/null # List installed tools volta list all
# Volta doesn't have a built-in cleanup command yet # (see https://github.com/volta-cli/volta/issues/330) # Manually clean cached downloads rm -rf ~/.volta/cache/ 2>/dev/null rm -rf ~/.volta/tmp/ 2>/dev/null # Remove old tool versions # List what's installed first ls ~/.volta/tools/image/node/ ls ~/.volta/tools/image/npm/ ls ~/.volta/tools/image/yarn/ # Remove specific old versions rm -rf ~/.volta/tools/image/node/16.20.2/ 2>/dev/null
pyenv keeps every Python version you've built from source — each one is 50-200MB depending on configuration.
# pyenv disk usage du -sh ~/.pyenv/ 2>/dev/null # Per-version sizes du -sh ~/.pyenv/versions/*/ 2>/dev/null | sort -hr # List installed versions pyenv versions # Check which version is active pyenv version
# Uninstall specific versions pyenv uninstall 3.9.7 pyenv uninstall 3.10.4 # Remove build cache (source tarballs) rm -rf ~/.pyenv/cache/ 2>/dev/null rm -rf ~/.pyenv/sources/ 2>/dev/null # Remove all versions except current current=$(pyenv version-name) for v in $(pyenv versions --bare | grep -v "^$current$"); do echo "Removing $v" pyenv uninstall -f "$v" done
~/.pyenv/sources/. Removing this directory saves space without affecting installed versions.
rbenv stores compiled Ruby versions and their gems:
# rbenv disk usage du -sh ~/.rbenv/ 2>/dev/null # Per-version sizes (includes gems) du -sh ~/.rbenv/versions/*/ 2>/dev/null | sort -hr # List installed versions rbenv versions # Check active version rbenv version
# Uninstall old Ruby versions rbenv uninstall 2.7.6 rbenv uninstall 3.0.4 # Clean ruby-build cache rm -rf ~/.rbenv/cache/ 2>/dev/null # Clean gems for a specific version RBENV_VERSION=3.1.2 gem cleanup # If using RVM instead: du -sh ~/.rvm/ 2>/dev/null rvm cleanup all rvm remove 2.7.6
SDKMAN is particularly space-hungry because JDK distributions are 200-400MB each, and it manages Gradle, Maven, Kotlin, and other JVM tools too:
# SDKMAN total disk usage du -sh ~/.sdkman/ 2>/dev/null # Per-candidate breakdown du -sh ~/.sdkman/candidates/*/ 2>/dev/null | sort -hr # JDK versions specifically du -sh ~/.sdkman/candidates/java/*/ 2>/dev/null | sort -hr # Download archives du -sh ~/.sdkman/archives/ 2>/dev/null # List installed candidates sdk list java sdk list gradle sdk list kotlin
# Remove download archives (biggest easy win) rm -rf ~/.sdkman/archives/* # Remove specific JDK versions sdk uninstall java 17.0.6-tem sdk uninstall java 11.0.19-amzn # Remove old Gradle/Maven versions sdk uninstall gradle 7.6.1 sdk uninstall maven 3.8.8 # SDKMAN flush command (clears temp + broadcast cache) sdk flush # See current defaults sdk current
archives/ directory alone can be 2-5GB.
asdf manages versions for many languages through plugins. Its disk usage is the sum of all managed runtimes:
# asdf total disk usage du -sh ~/.asdf/ 2>/dev/null # Per-plugin breakdown du -sh ~/.asdf/installs/*/ 2>/dev/null | sort -hr # Download cache du -sh ~/.asdf/downloads/ 2>/dev/null # List all installed versions asdf list
# Remove specific versions asdf uninstall nodejs 16.20.2 asdf uninstall python 3.9.7 # Remove download cache rm -rf ~/.asdf/downloads/ # Remove a whole plugin (and all its versions) asdf plugin remove erlang # List and clean old versions per plugin for plugin in $(asdf plugin list); do echo "=== $plugin ===" asdf list "$plugin" done
~/.local/share/mise/ for its cache directory.
Rust toolchains are large (500MB-1GB each) and Rust releases every 6 weeks, so old toolchains accumulate fast:
# rustup disk usage du -sh ~/.rustup/ 2>/dev/null # Per-toolchain sizes du -sh ~/.rustup/toolchains/*/ 2>/dev/null | sort -hr # List installed toolchains rustup toolchain list # List installed targets rustup target list --installed
# Remove old toolchains rustup toolchain uninstall nightly-2024-01-15 rustup toolchain uninstall 1.73.0 # Keep only stable and optionally nightly rustup update stable rustup toolchain uninstall nightly # Remove unused targets rustup target remove wasm32-unknown-unknown # Clean download cache rm -rf ~/.rustup/downloads/ 2>/dev/null rm -rf ~/.rustup/tmp/ 2>/dev/null # Also clean Cargo cache (related) cargo cache --autoclean 2>/dev/null || rm -rf ~/.cargo/registry/cache/
If you use goenv or manage multiple Go versions manually:
# goenv disk usage du -sh ~/.goenv/ 2>/dev/null # Manual Go SDK downloads (go install golang.org/dl/goX.Y.Z) du -sh ~/sdk/ 2>/dev/null du -sh ~/sdk/go*/ 2>/dev/null | sort -hr # Go module cache (not a version manager, but related) du -sh ~/go/pkg/mod/ 2>/dev/null du -sh $(go env GOMODCACHE) 2>/dev/null
# Remove goenv versions goenv uninstall 1.19.5 # Remove manual SDK installs rm -rf ~/sdk/go1.19.5/ rm -rf ~/sdk/go1.20.3/ # Clean Go module cache go clean -modcache # Clean Go build cache go clean -cache
Create a single script that audits and optionally cleans all version managers:
#!/bin/bash # version_manager_cleanup.sh — Audit all version managers echo "╔══════════════════════════════════════╗" echo "║ Version Manager Disk Audit ║" echo "╚══════════════════════════════════════╝" echo "" total=0 audit() { local name="$1" path="$2" if [ -d "$path" ]; then size=$(du -sm "$path" 2>/dev/null | awk '{print $1}') total=$((total + size)) printf " %-12s %6s MB %s\n" "$name" "$size" "$path" fi } audit "nvm" "$HOME/.nvm" audit "fnm" "$HOME/.fnm" audit "volta" "$HOME/.volta" audit "pyenv" "$HOME/.pyenv" audit "rbenv" "$HOME/.rbenv" audit "rvm" "$HOME/.rvm" audit "sdkman" "$HOME/.sdkman" audit "asdf" "$HOME/.asdf" audit "mise" "$HOME/.local/share/mise" audit "rustup" "$HOME/.rustup" audit "goenv" "$HOME/.goenv" audit "go-sdk" "$HOME/sdk" echo "" echo " Total: ${total} MB ($((total / 1024)) GB)" echo "" # Quick cleanup (caches only — doesn't remove versions) if [ "$1" = "--clean" ]; then echo "Cleaning caches..." rm -rf ~/.nvm/.cache/ 2>/dev/null rm -rf ~/Library/Caches/fnm_multishells/ 2>/dev/null rm -rf ~/.volta/cache/ ~/.volta/tmp/ 2>/dev/null rm -rf ~/.pyenv/cache/ ~/.pyenv/sources/ 2>/dev/null rm -rf ~/.rbenv/cache/ 2>/dev/null rm -rf ~/.sdkman/archives/ 2>/dev/null rm -rf ~/.asdf/downloads/ 2>/dev/null rm -rf ~/.rustup/downloads/ ~/.rustup/tmp/ 2>/dev/null echo "Cache cleanup done!" fi
Version managers are just part of the picture. ClearDisk monitors 44+ developer cache paths on macOS — including all version managers, build caches, Docker, Xcode DerivedData, Homebrew, and more — right from the menu bar.
Get ClearDisk (Free & Open Source)| Tool | Audit Command | Clean Command |
|---|---|---|
| nvm | du -sh ~/.nvm/ |
nvm uninstall <version> |
| fnm | du -sh ~/.fnm/ |
fnm uninstall <version> |
| Volta | du -sh ~/.volta/ |
rm -rf ~/.volta/cache/ |
| pyenv | du -sh ~/.pyenv/ |
pyenv uninstall <version> |
| rbenv | du -sh ~/.rbenv/ |
rbenv uninstall <version> |
| SDKMAN | du -sh ~/.sdkman/ |
sdk uninstall java <v> |
| asdf | du -sh ~/.asdf/ |
asdf uninstall <plugin> <v> |
| rustup | du -sh ~/.rustup/ |
rustup toolchain uninstall <v> |
| goenv | du -sh ~/.goenv/ |
goenv uninstall <version> |
| All caches | Run audit script above | ./version_manager_cleanup.sh --clean |
| GUI monitoring | brew tap bysiber/cleardisk && brew install --cask cleardisk |
|
SDKMAN typically uses the most because JDK distributions are 200-400MB each, and developers often install multiple vendors (Temurin, Corretto, GraalVM). A machine with 5-6 JDK versions can easily have 2-3GB in SDKMAN alone. rustup is a close second, with each Rust toolchain being 500MB-1GB.
Yes. Deleting cache and download directories (like ~/.sdkman/archives/, ~/.pyenv/cache/, ~/.asdf/downloads/) only removes cached downloads. Already-installed versions continue to work. The files will be re-downloaded if you reinstall a version.
Technically yes, but it can cause PATH conflicts and confusion. If you have both nvm and fnm installed, or both pyenv and conda, consider consolidating to one per language. Removing the unused ones saves disk space and reduces complexity.
This is a popular request across most version managers (see nvm#1195, asdf#830, fnm#696). Most tools treat version management as "install/uninstall only" and leave garbage collection to the user. Until cleanup commands are added, the manual approach in this guide is the way to go.
A quarterly cleanup is a good cadence. After major project transitions (e.g., upgrading from Node 18 to 20), immediately uninstall the old version while you remember. Setting up the automation script from section 11 as a cron job makes this effortless.
Yes. ClearDisk monitors 44+ developer cache paths on macOS, including nvm, pyenv, rbenv, SDKMAN, rustup, and other version manager directories. It shows real-time disk usage from the menu bar so you can see when a version manager cache starts growing.