Caching Maven and Gradle Dependencies in a DevContainer

JVM projects pull large dependency trees, and re-downloading them every rebuild is slow. This page caches the Maven ~/.m2 repository or the Gradle cache on a named volume, so a rebuild reuses downloaded artifacts and builds stay fast.

Prerequisites

You need a Java devcontainer using Maven or Gradle.

  • A Java Feature pinned to a JDK.
  • Maven or Gradle (ideally via the wrapper).
  • A named volume for the dependency store.

Cache prerequisitesYou need the store path, a volume, a warm step, and a reuse check.Store path~/.m2 or ~/.gradleVolumemount itWarmdownload depsVerifyreuse

Step-by-Step Implementation

  1. Mount the dependency store on a volume (Gradle shown).
{
  "mounts": ["source=devcontainer-gradle-cache,target=/home/vscode/.gradle,type=volume"],
  "remoteUser": "vscode"
}
  1. Or for Maven, cache ~/.m2.
{ "mounts": ["source=devcontainer-m2,target=/home/vscode/.m2,type=volume"] }
  1. Warm the cache on create.
{ "postCreateCommand": "./gradlew --no-daemon dependencies" }
  1. Verify a rebuild reuses the store.
./gradlew --no-daemon build --offline   # succeeds from cache

JVM cache anatomyA volume-backed dependency store with a wrapper-pinned build tool makes JVM builds fast.Dependency store~/.m2 or ~/.gradleNamed volumesurvives rebuildsWrapper-pinned toolreproducible resolutionResultfast, offline-capable

Common Pitfalls

Slow JVM builds come from an uncached store or missing wrapper.

JVM cache triageA triage path from re-downloading dependencies to a warm, wrapper-pinned build.Do deps re-download each build?YESMount the store on a volumeSame tool version everywhere?YESUse the wrapperFast JVM builds

SymptomRoot CauseRemediation
Dependencies re-download each buildStore not on a volumeMount ~/.m2 or ~/.gradle on a volume
Tool version variesNot using the wrapperRun via mvnw/gradlew
Cache permission errorsVolume owned by rootchown the store to the remote user
Offline build failsCache not warmedWarm it in postCreateCommand

Conclusion

Cache the dependency store on a named volume — ~/.m2 for Maven or ~/.gradle for Gradle — and run through the wrapper so the build-tool version is pinned. A rebuild then reuses downloaded artifacts, and JVM dependency trees stop slowing every build.

Cache and pinCache the store and pin the tool via the wrapper for fast, reproducible builds.Cache~/.m2 or ~/.gradleOn a named volumeWarmed on createPinJDK versionWrapper tool versionCommitted build files

FAQ

Which directory should I cache, Maven or Gradle? Cache the one your build uses: ~/.m2/repository for Maven, or ~/.gradle for Gradle. Mount it on a named volume so downloaded artifacts persist across rebuilds. If you use both tools, cache both directories.

Does caching change build reproducibility? No, as long as you build through the wrapper (mvnw/gradlew), which pins the tool version, and your build files pin dependency versions. The cache only holds artifacts your build already resolved, so it speeds builds without altering results.

Why do I get permission errors on the cache volume? The volume is owned by root but your remoteUser isn't. chown the cache directory to the remote user (in a hook or the Dockerfile), and the non-root build can read and write it.