Gradle build cache tutorial: see FROM-CACHE with a self-hosted cache
Start a cache server, point a small Gradle project at it, build twice, and watch the second build take its work from the cache. Everything below was run for real: release 0.2.1 of the server and Gradle 9.8.0.
1. Start the server
Download and verify the release as shown in Verify a release yourself, unpack it, and run it with a password and a size cap:
FSCACHE_ADDR=127.0.0.1:8080 FSCACHE_DATA_DIR=./data \
FSCACHE_USERNAME=gradle FSCACHE_PASSWORD=demo-pass \
FSCACHE_MAX_BYTES=1073741824 ./fscache
Prefer a container? Gradle build cache with Docker Compose has the same settings. (My test run used port 18090 instead of 8080; the outputs below do not show the port.)
2. A small project
Four files. settings.gradle.kts says where the cache is:
rootProject.name = "demo"
// FosterStack Cache — remote Gradle build cache.
// https://github.com/fosterstack/cache
buildCache {
local { isEnabled = false } // so a hit can only come from the remote
remote<HttpBuildCache> {
url = uri("http://127.0.0.1:8080/")
isPush = true
credentials {
username = "gradle"
password = System.getenv("FSCACHE_PASSWORD")
}
}
}
Turning the local cache off is only for this test. It means a hit can only have come from the remote. build.gradle.kts is one line, plugins { java }. gradle.properties switches caching on, which the buildCache block does not do by itself:
org.gradle.caching=true
And src/main/java/demo/App.java is any small class.
3. Build once
export FSCACHE_PASSWORD=demo-pass
gradle build --build-cache
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :jar
> Task :assemble
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build
BUILD SUCCESSFUL in 5s
2 actionable tasks: 2 executed
Nothing came from the cache, and every cacheable task ran. Gradle stored the compile result on the server. The status page agrees:
$ curl -s -u gradle:demo-pass localhost:8080/statusz | grep -E '"store_entries"|"cache_hits"|"cache_misses"'
"store_entries": 1,
"cache_hits": 0,
"cache_misses": 1,
4. Clean, and build again
gradle clean
gradle build --build-cache
> Task :compileJava FROM-CACHE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar
> Task :assemble
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build
BUILD SUCCESSFUL in 5s
2 actionable tasks: 1 executed, 1 from cache
There it is: :compileJava FROM-CACHE and "1 from cache". The server counted the hit:
$ curl -s -u gradle:demo-pass localhost:8080/statusz | grep -E '"store_entries"|"cache_hits"|"cache_misses"'
"store_entries": 1,
"cache_hits": 1,
"cache_misses": 1,
The one thing that tripped me up
My first attempt failed before any build ran, because I used http://localhost:...:
FAILURE: Build failed with an exception.
* What went wrong:
Using insecure protocols with remote build cache, without explicit opt-in, is unsupported.
Gradle refuses plain http:// unless you set isAllowInsecureProtocol = true. With Gradle 9.8.0, the address 127.0.0.1 was allowed without it, but the name localhost was refused, with or without a credentials block. That is why the file above uses 127.0.0.1. For a real server, use HTTPS and skip the opt-in. Gradle sends the password with every request.
If your second build shows no FROM-CACHE, work through Gradle remote build cache not working. The status codes are explained in Gradle build cache HTTP status codes.