From 9e597fa052ec31a89b0d6d1449c0a7d26918cb1e Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:17:46 -0400 Subject: [PATCH 1/5] Page: Jenkins and CI build cache for Gradle and Maven --- jenkins-ci-build-cache/index.html | 137 ++++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 141 insertions(+) create mode 100644 jenkins-ci-build-cache/index.html diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html new file mode 100644 index 0000000..2134259 --- /dev/null +++ b/jenkins-ci-build-cache/index.html @@ -0,0 +1,137 @@ + + + + + +Jenkins and CI build cache for Gradle and Maven + + + + + + + + +
+ +
Infrastructure worth keeping — and kept patched.
+
+
+

Jenkins and CI build cache for Gradle and Maven

+

Point your Jenkins agents, GitHub Actions runners or GitLab runners at one shared build cache, so a fresh agent stops rebuilding what another agent already built.

+ +

How a CI runner uses a remote build cache

+

The cache is a plain web server. Your build tool (Gradle, or Maven with Apache's Build Cache Extension) asks it for finished task results and uploads new ones. Any runner that can reach the server over HTTP can use it. There is no plugin to install on the CI system.

+

We have not tested it with any particular CI product. Our own tests run in GitHub Actions. The protocol is the same everywhere, so what matters is that the runner can reach the server and holds the password.

+ +

Let CI write, let developers read

+

The simplest safe setup is that only CI uploads to the cache and laptops only download. In settings.gradle.kts:

+
isPush = System.getenv("CI") != null
+

This is the Gradle client choosing not to upload. It is not a permission on the server. Today the server has one password, and anyone holding it can write. Share it only with people and systems you already trust to change your code.

+ +

Runners inside your Kubernetes cluster

+

If your runners already run in the cluster that hosts the cache (including Jenkins agents), you need no load balancer and no ingress. Point the build at the cluster-internal name:

+
// FosterStack Cache — remote Gradle build cache.
+// https://github.com/fosterstack/cache
+buildCache {
+    remote<HttpBuildCache> {
+        url = uri("http://fscache.fscache-ns.svc.cluster.local/")
+        isAllowInsecureProtocol = true
+        isPush = true
+        credentials {
+            username = System.getenv("FSCACHE_USERNAME")
+            password = System.getenv("FSCACHE_PASSWORD")
+        }
+    }
+}
+

Replace fscache-ns with your namespace. Plain HTTP is acceptable here only because the traffic never leaves the cluster network. The manifests are in the Kubernetes guide.

+ +

Runners outside the cluster, or on plain servers

+

Run the server on a host the runners can reach and put HTTPS in front of it. Gradle sends the password with every request, so over plain HTTP it travels in the clear. Gradle refuses plain HTTP to anything but localhost unless you opt in, which is a useful warning, not a nuisance. Setup is in the install guide.

+ +

Give the runner the password

+

Store it in your CI system's secret store and expose it as the FSCACHE_PASSWORD environment variable. On a long-lived agent, Gradle's background daemon reads its environment once, when it starts. If you change the secret and builds quietly stop using the cache, run ./gradlew --stop so the next build starts a fresh daemon.

+ +

Check that CI is really using the cache

+

Run the same build twice on two different agents. The second should show FROM-CACHE next to tasks. A runner's local cache can hide a broken remote, so run once with -i, which prints remote errors. Gradle treats a broken remote as non-fatal, so it fails silently otherwise. On the server, the hit and miss counters at /metrics should move.

+

Maven on CI follows the same pattern with the extension and a <server> entry for the password. The server side for Maven is built; its acceptance testing is still in progress. See the Gradle and Maven setup page.

+ +
+ Gradle and Maven setup + FosterStack home +
+
+ + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..9762aef 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/jenkins-ci-build-cache/ + 2026-10-08 + From 60d4551b6cec18dd5ac37b8ed3607516ac70fbd1 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:23:28 -0400 Subject: [PATCH 2/5] CI page: the server has a read-only login; say so --- jenkins-ci-build-cache/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index 2134259..a8d6d1c 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -86,9 +86,9 @@

How a CI runner uses a remote build cache

We have not tested it with any particular CI product. Our own tests run in GitHub Actions. The protocol is the same everywhere, so what matters is that the runner can reach the server and holds the password.

Let CI write, let developers read

-

The simplest safe setup is that only CI uploads to the cache and laptops only download. In settings.gradle.kts:

+

The simplest safe setup is that only CI uploads to the cache and laptops only download. The server can enforce this: give laptops the optional read-only login (FSCACHE_RO_USERNAME and FSCACHE_RO_PASSWORD), and any write they attempt is refused. On the Gradle side you can also stop uploads from non-CI machines in settings.gradle.kts:

isPush = System.getenv("CI") != null
-

This is the Gradle client choosing not to upload. It is not a permission on the server. Today the server has one password, and anyone holding it can write. Share it only with people and systems you already trust to change your code.

+

That line alone is the client choosing not to upload, not a rule on the server. Anyone holding the read-write password can write, so share it only with people and systems you already trust to change your code.

Runners inside your Kubernetes cluster

If your runners already run in the cluster that hosts the cache (including Jenkins agents), you need no load balancer and no ingress. Point the build at the cluster-internal name:

From 4d4950b1f6457ed26f1a74d3b726d9a49fe15028 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:25:24 -0400 Subject: [PATCH 3/5] CI page: a login, not the password --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index a8d6d1c..d3ccfc9 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -83,7 +83,7 @@

Jenkins and CI build cache for Gradle and Maven

How a CI runner uses a remote build cache

The cache is a plain web server. Your build tool (Gradle, or Maven with Apache's Build Cache Extension) asks it for finished task results and uploads new ones. Any runner that can reach the server over HTTP can use it. There is no plugin to install on the CI system.

-

We have not tested it with any particular CI product. Our own tests run in GitHub Actions. The protocol is the same everywhere, so what matters is that the runner can reach the server and holds the password.

+

We have not tested it with any particular CI product. Our own tests run in GitHub Actions. The protocol is the same everywhere, so what matters is that the runner can reach the server and holds a login.

Let CI write, let developers read

The simplest safe setup is that only CI uploads to the cache and laptops only download. The server can enforce this: give laptops the optional read-only login (FSCACHE_RO_USERNAME and FSCACHE_RO_PASSWORD), and any write they attempt is refused. On the Gradle side you can also stop uploads from non-CI machines in settings.gradle.kts:

From bd477b68a6c3ebc36bf7dc22ea9eb0f7a6eb2c86 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:35:36 -0400 Subject: [PATCH 4/5] Owner cold-read edit: Maven stated without the in-progress hedge --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index d3ccfc9..25eecbe 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -115,7 +115,7 @@

Give the runner the password

Check that CI is really using the cache

Run the same build twice on two different agents. The second should show FROM-CACHE next to tasks. A runner's local cache can hide a broken remote, so run once with -i, which prints remote errors. Gradle treats a broken remote as non-fatal, so it fails silently otherwise. On the server, the hit and miss counters at /metrics should move.

-

Maven on CI follows the same pattern with the extension and a <server> entry for the password. The server side for Maven is built; its acceptance testing is still in progress. See the Gradle and Maven setup page.

+

Maven on CI follows the same pattern with the extension and a <server> entry for the password. See the Gradle and Maven setup page.

Gradle and Maven setup From 7e3ae2c085813961f020d69bbbb8ceda48318211 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:21:50 -0400 Subject: [PATCH 5/5] Plain-HTTP wording: tested on Gradle 9.8.0, localhost name refused, 127.0.0.1 allowed --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index 25eecbe..194265b 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -108,7 +108,7 @@

Runners inside your Kubernetes cluster

Replace fscache-ns with your namespace. Plain HTTP is acceptable here only because the traffic never leaves the cluster network. The manifests are in the Kubernetes guide.

Runners outside the cluster, or on plain servers

-

Run the server on a host the runners can reach and put HTTPS in front of it. Gradle sends the password with every request, so over plain HTTP it travels in the clear. Gradle refuses plain HTTP to anything but localhost unless you opt in, which is a useful warning, not a nuisance. Setup is in the install guide.

+

Run the server on a host the runners can reach and put HTTPS in front of it. Gradle sends the password with every request, so over plain HTTP it travels in the clear. Gradle refuses plain HTTP unless you opt in with isAllowInsecureProtocol, which is a useful warning, not a nuisance. (In our test with Gradle 9.8.0, the address 127.0.0.1 was allowed without it, but the name localhost was refused.) Setup is in the install guide.

Give the runner the password

Store it in your CI system's secret store and expose it as the FSCACHE_PASSWORD environment variable. On a long-lived agent, Gradle's background daemon reads its environment once, when it starts. If you change the secret and builds quietly stop using the cache, run ./gradlew --stop so the next build starts a fresh daemon.