If you develop Java, Scala, or Kotlin on macOS, you've likely never thought about the ~/.m2/repository directory. That's by design -- Maven downloads dependencies silently and stores them forever. After a year or two of active JVM development, this directory alone can consume 5-30GB of your SSD.
Add sbt's Ivy cache, Coursier cache, and build output directories, and JVM development can easily eat 40-60GB of disk space across all your projects. This guide covers how to find, audit, and safely clean every JVM build tool cache on macOS.
Where Maven Stores Everything on macOS
Maven's local repository is a directory tree mirroring the Maven Central coordinates (groupId/artifactId/version). Every dependency you've ever used gets cached here permanently:
| Location | What's Inside | Typical Size |
|---|---|---|
| ~/.m2/repository | All downloaded JARs, POMs, sources, javadocs | 5-20GB |
| ~/.m2/repository/.cache | Maven resolver metadata cache | 100-500MB |
| ~/.m2/wrapper | Maven wrapper distributions | 200-800MB |
| ~/.m2/settings.xml | Configuration (keep this!) | Tiny |
| project/target/ | Build outputs per project | 500MB-3GB each |
Quick Audit: How Much Space Are You Losing?
Run this to get the full picture of JVM cache usage on your Mac:
# Maven local repository
du -sh ~/.m2/repository 2>/dev/null
# Maven wrapper distributions
du -sh ~/.m2/wrapper/dists 2>/dev/null
# sbt caches
du -sh ~/.sbt 2>/dev/null
du -sh ~/.ivy2 2>/dev/null
# Coursier cache (used by sbt, scala-cli, cs)
du -sh ~/.cache/coursier 2>/dev/null
du -sh ~/Library/Caches/Coursier 2>/dev/null
# Scala/Metals/Bloop caches
du -sh ~/.bloop 2>/dev/null
du -sh ~/.cache/metals 2>/dev/null
# Kotlin daemon & caches
du -sh ~/.kotlin/daemon 2>/dev/null
# Find ALL target/ dirs across projects
find ~/Projects -name "target" -type d -maxdepth 4 -exec du -sh {} + 2>/dev/null | sort -rh | head -20
# TOTAL snapshot of all JVM caches
echo "=== JVM Build Tool Disk Usage ==="
for dir in ~/.m2 ~/.sbt ~/.ivy2 ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.kotlin; do
[ -d "$dir" ] && echo "$(du -sh "$dir" 2>/dev/null)"
done
Cleaning Maven's Local Repository
1. Remove Old SNAPSHOT Versions
SNAPSHOT dependencies are development versions that change frequently. Old snapshots are never cleaned automatically:
# Find all SNAPSHOT directories
find ~/.m2/repository -type d -name "*SNAPSHOT*" -exec du -sh {} + 2>/dev/null | sort -rh
# Remove all SNAPSHOTs (safe -- they'll re-download)
find ~/.m2/repository -type d -name "*SNAPSHOT*" -exec rm -rf {} + 2>/dev/null
# Remove SNAPSHOT JARs older than 60 days
find ~/.m2/repository -path "*SNAPSHOT*" -name "*.jar" -mtime +60 -delete
2. Remove Old Versions of Dependencies
When dependencies get updated, the old version stays in .m2 forever. You can clean versions you haven't used recently:
# Find JARs not accessed in 90+ days
find ~/.m2/repository -name "*.jar" -atime +90 -exec du -sh {} + 2>/dev/null | sort -rh | head -30
# Remove dependency files not accessed in 6 months
find ~/.m2/repository -name "*.jar" -atime +180 -delete
find ~/.m2/repository -name "*.pom" -atime +180 -delete
# Clean up empty directories afterward
find ~/.m2/repository -type d -empty -delete
3. Maven Dependency Purge Plugin
Maven has a built-in plugin for cleaning the local repository intelligently:
# Purge all SNAPSHOTs from local repo
mvn dependency:purge-local-repository -DsnapshotsOnly=true
# Re-resolve and download fresh copies
mvn dependency:purge-local-repository -DreResolve=true
# Purge specific groups
mvn dependency:purge-local-repository \
-DmanualInclude=com.example:my-lib
# List what would be purged (dry run)
mvn dependency:purge-local-repository -DactTransitively=false -Dverbose=true
4. Clean Maven Wrapper Distributions
# Check wrapper distributions
du -sh ~/.m2/wrapper/dists/*
# Remove all but keep settings.xml
rm -rf ~/.m2/wrapper/dists/*
# Remove metadata cache
rm -rf ~/.m2/repository/.cache
Cleaning sbt Caches (Scala Developers)
sbt uses Ivy for dependency resolution (sbt 0.x/1.x) and Coursier (sbt 1.3+). Both download dependencies independently of Maven:
| Location | What's Inside | Safe to Delete? |
|---|---|---|
| ~/.sbt | sbt boot, plugins, global settings | Caches yes, settings no |
| ~/.ivy2/cache | Ivy-resolved dependency JARs | Yes (re-downloads) |
| ~/.ivy2/local | Locally published artifacts | Maybe (check first) |
| ~/.cache/coursier | Coursier dependency cache | Yes (re-downloads) |
| ~/Library/Caches/Coursier | Coursier macOS cache | Yes (re-downloads) |
| ~/.bloop | Bloop build server cache | Yes (regenerates) |
| ~/.cache/metals | Metals LSP cache (IntelliJ/VSCode) | Yes (re-indexes) |
# Clean sbt boot & plugin caches
rm -rf ~/.sbt/boot
rm -rf ~/.sbt/1.0/plugins/target
rm -rf ~/.sbt/1.0/target
# Clean Ivy cache
rm -rf ~/.ivy2/cache
rm -rf ~/.ivy2/local # Only if you don't publishLocal
# Clean Coursier cache
rm -rf ~/.cache/coursier/v1
rm -rf ~/Library/Caches/Coursier/v1
# Clean Bloop & Metals
rm -rf ~/.bloop
rm -rf ~/.cache/metals
# Using coursier CLI
cs cleanup # if cs is installed
Cleaning Kotlin & Build Server Caches
# Kotlin daemon files
du -sh ~/.kotlin/daemon 2>/dev/null
rm -rf ~/.kotlin/daemon
# Kotlin/Native cache
du -sh ~/.konan 2>/dev/null
rm -rf ~/.konan/cache
rm -rf ~/.konan/dependencies
# IntelliJ caches (from JVM development)
du -sh ~/Library/Caches/JetBrains/ 2>/dev/null
# IntelliJ local history & index
du -sh ~/Library/Caches/JetBrains/IntelliJIdea*/caches 2>/dev/null
Bulk Cleaning target/ Directories
Every Maven and sbt project creates a target/ directory for compiled classes, packaged JARs, and intermediate build outputs. These add up fast:
# Find all JVM target/ directories, sorted by size
find ~/Projects -name "target" -type d \
\( -path "*/src/*" -prune -o -print \) \
-exec du -sh {} + 2>/dev/null | sort -rh | head -20
# Delete target/ in projects not touched in 30+ days
find ~/Projects -name "target" -type d -maxdepth 4 -mtime +30 \
-exec echo "Removing: {}" \; -exec rm -rf {} + 2>/dev/null
# Maven multi-module: clean all sub-modules
cd ~/Projects/my-project && mvn clean
# sbt equivalent
cd ~/Projects/my-scala-project && sbt clean
Using ClearDisk to Monitor JVM Caches
ClearDisk -- Free, Open-Source Cache Monitor for macOS
Instead of running audit commands manually, ClearDisk sits in your menu bar and monitors all JVM caches alongside other developer tool caches:
- Maven ~/.m2/repository
- Gradle ~/.gradle/caches
- sbt/Ivy/Coursier caches
- Kotlin/Native ~/.konan
- Plus: Xcode DerivedData, node_modules, Docker, Rust, Go, pip, Homebrew
View on GitHub -- MIT License, Swift/SwiftUI, macOS 14+
Install: brew tap bysiber/cleardisk && brew install --cask cleardisk
Automated Monthly Cleanup Script
Save this as ~/.local/bin/jvm-cache-cleanup.sh and run it monthly:
#!/bin/bash
# JVM Build Tool Cache Cleanup Script for macOS
# Safely cleans Maven, sbt, Coursier, and build outputs
set -euo pipefail
echo "=== JVM Cache Audit ==="
# Pre-cleanup sizes
echo "--- Current Sizes ---"
for dir in ~/.m2/repository ~/.sbt ~/.ivy2 ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.konan ~/.kotlin; do
[ -d "$dir" ] && echo "$(du -sh "$dir" 2>/dev/null)"
done
TOTAL_BEFORE=$(du -shc ~/.m2/repository ~/.sbt ~/.ivy2 ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.konan ~/.kotlin 2>/dev/null | tail -1 | cut -f1)
echo "Total before: $TOTAL_BEFORE"
# 1. Remove SNAPSHOTs older than 30 days
echo -e "\n--- Cleaning old SNAPSHOTs ---"
find ~/.m2/repository -path "*SNAPSHOT*" -name "*.jar" -mtime +30 -delete 2>/dev/null
find ~/.m2/repository -type d -name "*SNAPSHOT*" -empty -delete 2>/dev/null
# 2. Remove Maven metadata cache
rm -rf ~/.m2/repository/.cache 2>/dev/null
# 3. Clean Ivy cache (sbt re-downloads via Coursier)
rm -rf ~/.ivy2/cache 2>/dev/null
# 4. Clean Coursier cache entries older than 60 days
find ~/.cache/coursier -type f -mtime +60 -delete 2>/dev/null
find ~/Library/Caches/Coursier -type f -mtime +60 -delete 2>/dev/null
# 5. Clean Bloop & Metals
rm -rf ~/.bloop 2>/dev/null
rm -rf ~/.cache/metals 2>/dev/null
# 6. Clean Kotlin daemon
rm -rf ~/.kotlin/daemon 2>/dev/null
# 7. Clean stale target/ directories (30+ days old)
echo -e "\n--- Cleaning stale target/ dirs ---"
find ~/Projects -name "target" -type d -maxdepth 4 -mtime +30 \
-exec echo " Removed: {}" \; -exec rm -rf {} + 2>/dev/null
# Post-cleanup sizes
echo -e "\n--- After Cleanup ---"
for dir in ~/.m2/repository ~/.sbt ~/.ivy2 ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.konan ~/.kotlin; do
[ -d "$dir" ] && echo "$(du -sh "$dir" 2>/dev/null)"
done
TOTAL_AFTER=$(du -shc ~/.m2/repository ~/.sbt ~/.ivy2 ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.konan ~/.kotlin 2>/dev/null | tail -1 | cut -f1)
echo -e "\nTotal after: $TOTAL_AFTER (was: $TOTAL_BEFORE)"
echo "Done! JVM caches cleaned."
How Maven .m2 Grows Over Time
Maven's local repository grows because:
- Every version is kept: When a dependency updates from 2.3.1 to 2.3.2, the old version stays forever
- Transitive dependencies: A single dependency can pull in dozens of transitive dependencies, each cached
- Multiple classifiers: JARs, source JARs, javadoc JARs, test JARs -- all cached separately
- SNAPSHOTs multiply: Each SNAPSHOT build gets timestamped copies that never expire
- Cross-tool overlap: Maven, Gradle, sbt may each cache the same dependency independently
A typical Spring Boot project can add 300-500MB to .m2 from dependencies alone. After working on 10+ projects over a year, 10GB+ is common.
The Cross-Tool Duplication Problem
The worst part of JVM build tool caches is duplication. If you use multiple build tools, the same JARs get cached multiple times:
| Dependency | Maven Location | Gradle Location | Coursier Location |
|---|---|---|---|
| guava-32.1.jar | ~/.m2/repository/com/google/guava/ | ~/.gradle/caches/modules-2/ | ~/.cache/coursier/v1/ |
| jackson-core-2.15.jar | ~/.m2/repository/com/fasterxml/ | ~/.gradle/caches/modules-2/ | ~/.cache/coursier/v1/ |
| slf4j-api-2.0.jar | ~/.m2/repository/org/slf4j/ | ~/.gradle/caches/modules-2/ | ~/.cache/coursier/v1/ |
Each popular library gets stored 2-3 times across different cache directories. There's no deduplication between Maven, Gradle, and Coursier.
IntelliJ IDEA Cache Cleanup
If you use IntelliJ for JVM development, its caches compound the problem:
# IntelliJ caches
du -sh ~/Library/Caches/JetBrains/IntelliJIdea* 2>/dev/null
du -sh ~/Library/Caches/JetBrains/IdeaIC* 2>/dev/null
# IntelliJ local index (can be huge)
du -sh ~/Library/Caches/JetBrains/IntelliJIdea*/caches 2>/dev/null
# IntelliJ system (logs, plugins)
du -sh ~/Library/Application\ Support/JetBrains/ 2>/dev/null
# Safe to clean: invalidate caches from within IntelliJ
# File -> Invalidate Caches -> Invalidate and Restart
# Or manually remove old version caches
# (keep only the latest version directory)
ls -la ~/Library/Caches/JetBrains/
Quick Reference Cheatsheet
# === MAVEN ===
du -sh ~/.m2/repository # check size
rm -rf ~/.m2/repository/.cache # metadata cache
mvn dependency:purge-local-repository # smart purge
find ~/.m2 -name "*SNAPSHOT*" -type d -exec rm -rf {} + # old snapshots
# === SBT ===
rm -rf ~/.ivy2/cache # Ivy cache
rm -rf ~/.sbt/boot # sbt boot files
rm -rf ~/.cache/coursier/v1 # Coursier cache
rm -rf ~/Library/Caches/Coursier/v1 # macOS Coursier
# === KOTLIN ===
rm -rf ~/.kotlin/daemon # daemon files
rm -rf ~/.konan/cache # K/N cache
# === BUILD SERVERS ===
rm -rf ~/.bloop # Bloop cache
rm -rf ~/.cache/metals # Metals cache
# === BUILD OUTPUTS ===
find ~/Projects -name "target" -type d -maxdepth 4 -exec du -sh {} + 2>/dev/null | sort -rh
# === NUCLEAR (ALL JVM CACHES) ===
rm -rf ~/.m2/repository ~/.ivy2/cache ~/.cache/coursier ~/Library/Caches/Coursier ~/.bloop ~/.cache/metals ~/.kotlin/daemon ~/.konan/cache
Frequently Asked Questions
How much space can I recover from Maven caches?
Typically 5-20GB from ~/.m2/repository alone. If you add sbt/Coursier/Kotlin caches and target/ directories, total recovery can reach 30-50GB for active JVM developers.
Is it safe to delete ~/.m2/repository?
Yes. It only contains downloaded dependencies. Maven re-downloads everything on the next build. Your first build will be slower, but nothing breaks permanently. Keep ~/.m2/settings.xml intact -- it holds your configuration.
Will cleaning .m2 affect Gradle?
Only if your Gradle builds use mavenLocal(). Gradle has its own cache at ~/.gradle/caches/. See our Gradle Cleanup Guide for that.
How do I prevent .m2 from growing so large?
Unfortunately, Maven has no built-in cache eviction. The best approach is periodic cleanup -- monthly for SNAPSHOTs, quarterly for the full repository. The dependency:purge-local-repository plugin helps but only works within a single project context.
What's the difference between .ivy2 and .cache/coursier?
Ivy is the older resolution engine used by sbt 1.2 and below. Coursier is the modern replacement used by sbt 1.3+. If you've upgraded sbt, the .ivy2 cache is likely stale and can be safely deleted.
Does ClearDisk handle all these caches?
ClearDisk monitors the common JVM cache locations (~/.m2, ~/.gradle, target/ directories) along with 15+ other developer tool caches. It's a free, open-source macOS menu bar app. Get it on GitHub.