Migrate from Build Cache Node: Gradle and Maven setup
Stand up a replacement cache, change one URL, check that it works. About 30 minutes for Gradle. There is no data to move.
Before you start
A build cache holds copies of things your builds can always rebuild, so there is nothing to export from the old node. Your first build on the new server just fills it. If you have not read why the node is going away, see Build Cache Node end of life.
Step 1: Run the new cache server
docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot ghcr.io/fosterstack/cache:latest
For anything beyond a first look, pin a specific version instead of latest. This command listens on this machine only. For other machines to use it, put an HTTPS proxy in front. Other ways to run it: bare binary with systemd, Kubernetes, or a disconnected network.
If the server can be reached by anyone you do not trust, turn on Basic Auth. Anyone who can write to the cache can change what your later builds use.
Step 2: Gradle build cache setup
Turn the cache on, in gradle.properties:
org.gradle.caching=true
Then point Gradle at the new server in settings.gradle.kts. If you used the old node, you only change the URL:
// FosterStack Cache — remote Gradle build cache.
// https://github.com/fosterstack/cache
buildCache {
remote<HttpBuildCache> {
url = uri("https://cache.example.com/")
isPush = true
credentials {
username = "gradle"
password = System.getenv("FSCACHE_PASSWORD")
}
}
}
Or in settings.gradle (Groovy):
// FosterStack Cache — remote Gradle build cache.
// https://github.com/fosterstack/cache
buildCache {
remote(HttpBuildCache) {
url = 'https://cache.example.com/'
push = true
credentials {
username = 'gradle'
password = System.getenv('FSCACHE_PASSWORD')
}
}
}
Keep the comment lines: a cache block that points at an unfamiliar host looks like data theft to a reviewer without them. Keep the trailing slash on the URL. Leave the credentials block in even if the server has no password yet; it is ignored then.
Step 3: Maven build cache setup
Maven uses Apache's Build Cache Extension. FosterStack Cache implements the server side of it.
Add the extension in .mvn/extensions.xml (check Maven Central for the current version):
<extensions>
<extension>
<groupId>org.apache.maven.extensions</groupId>
<artifactId>maven-build-cache-extension</artifactId>
<version>1.2.3</version>
</extension>
</extensions>
Point it at the server in .mvn/maven-build-cache-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0 https://maven.apache.org/xsd/build-cache-config-1.2.0.xsd">
<configuration>
<enabled>true</enabled>
<!-- FosterStack Cache — remote Maven build cache.
https://github.com/fosterstack/cache -->
<remote enabled="true" saveToRemote="true" id="fosterstack-cache">
<url>https://cache.example.com/</url>
</remote>
</configuration>
</cache>
The remote element must be inside configuration, and saveToRemote="true" is what makes the machine upload; without it the server stays empty. If the server uses Basic Auth, add a matching <server> with the same id (fosterstack-cache) in ~/.m2/settings.xml. Then run mvn install.
The extension itself is Apache's project. If your hit rate looks low, start with the extension's own configuration guide.
Step 4: Check that it works
For Gradle, run the build twice:
./gradlew build --build-cache
On the second run, look for FROM-CACHE next to tasks. Gradle also has a local cache that gives hits even when the remote is broken, so to test the remote itself, clear the local one and run with -i:
rm -rf ~/.gradle/caches/build-cache-1
./gradlew --stop
./gradlew build --build-cache -i
On the server, the hit and miss counters should move:
curl -s https://cache.example.com/metrics | grep -E 'fscache_cache_(hits|misses)_total'
For Maven, build twice from a clean checkout and look for cache hits in the log for unchanged modules.
Step 5: Retire the old node
Once builds hit the new server, stop the old Build Cache Node container.
Security is the difference
Security is what sets this product apart. The release items below ship in the current release. The supply-chain items describe how we work today, in our own build and CI. You can check each one yourself.
Signed and verifiable
- Signed releases. Images and the checksums file are signed with Sigstore, with no signing key stored anywhere. How to verify.
- Build provenance. Every image and binary archive carries a signed statement naming the CI run that built it.
- A bill of materials. Every binary archive ships with an SBOM.
- Published vulnerability statements. A VEX file with each release says which known vulnerabilities do and do not affect it.
- Reproducible. Each image is assembled a second time on a separate machine, and the digests must match or the release fails.
Supply chain
- Pinned build steps. Every GitHub Action our releases use is pinned to an exact commit, and a check in CI enforces it on every change. The container base image is pinned by digest.
- Hash-checked installs. Python tooling in our CI is installed with hash checking, so a swapped package is refused.
- A 7-day wait. Our CI holds new dependency versions for 7 days before adopting them, and Dependabot has the same cooling-off period.
- A daily supply-chain check. Once a day, everything our build and CI pull in (actions, tools, packages) is checked against public advisory databases and malicious-package reports. Any hit opens an issue.
- Scanned before release. Two independent scanners with different databases check every release candidate, and any finding blocks the release unless a published VEX statement explains it. RELEASING.md.
- Rescanned every day. Every published image is scanned again daily, so a new vulnerability in something we shipped raises an issue.
Small, quiet and patched
- Tiny and quiet. A distroless image with one static program and no shell. It sends no telemetry and makes no outbound connections.
- A FIPS build. A FIPS variant uses Go's validated FIPS 140-3 cryptographic module.
- Patched for everyone. We aim to ship dependency fixes within 48 hours of disclosure. That is a target, not a promise. Security patches are never withheld from the free tier.
- Report a problem privately. See the security policy.
What you give up
The old node had a small web page with a purge button. FosterStack Cache has no settings page. You set it with flags and environment variables, and a read-only status page shows size and hit counts. To empty the cache, remove its volume and start again.
Full details: Gradle docs, Maven docs, migration guide on GitHub.