Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions monitor-build-cache-prometheus-grafana/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Monitor a Gradle build cache with Prometheus and Grafana</title>
<meta name="description" content="Prometheus metrics, example queries, a ready-made Grafana dashboard and the status page for a self-hosted Gradle and Maven build cache. Metric names checked on a running 0.2.1 server.">
<link rel="canonical" href="https://fosterstack.com/monitor-build-cache-prometheus-grafana/">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<style>
:root {
--bg: #ffffff; --fg: #16181d; --muted: #5b6472; --line: #e4e7ec;
--accent: #0d7a5f; --accent-fg: #ffffff; --card: #f6f8f9; --warn-bg: #fff7ed; --warn-line: #fdba74;
--mark: #0f4d66;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #101318; --fg: #e8eaf0; --muted: #98a2b3; --line: #262c36;
--accent: #2fbf95; --accent-fg: #08110e; --card: #171c23; --warn-bg: #241a10; --warn-line: #7c4a12;
--mark: #e8eaf0;
}
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg); color: var(--fg);
font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
-webkit-font-smoothing: antialiased;
}
main { max-width: 44rem; margin: 0 auto; padding: 0 1.25rem 4rem; }
header.site { max-width: 44rem; margin: 0 auto; padding: 1.25rem; display: flex; align-items: baseline; gap: .6rem; }
.logo { font-weight: 700; font-size: 1.05rem; letter-spacing: -.01em; }
.logo span { color: var(--accent); }
.logo .mark { width: 1.2em; height: 1.2em; vertical-align: -.24em; margin-right: .4em; }
.tag { color: var(--muted); font-size: .85rem; }
.notice {
background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: 8px;
padding: .7rem 1rem; font-size: .92rem; margin: 1.5rem 0 2.5rem;
}
h1 { font-size: 2rem; line-height: 1.2; letter-spacing: -.02em; margin-bottom: 1rem; }
.lede { font-size: 1.12rem; color: var(--muted); margin-bottom: 2rem; }
h2 { font-size: 1.25rem; margin: 3rem 0 .9rem; letter-spacing: -.01em; }
p + p { margin-top: .8rem; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .8rem; margin-top: 1rem; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 1rem 1.1rem; }
.card h3 { font-size: .98rem; margin-bottom: .35rem; }
.card p { font-size: .9rem; color: var(--muted); }
ul.trust { list-style: none; margin-top: .5rem; }
ul.trust li { padding: .45rem 0 .45rem 1.6rem; position: relative; }
ul.trust li::before { content: "✓"; position: absolute; left: .2rem; color: var(--accent); font-weight: 700; }
code, pre { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .88em; }
code { background: var(--card); border: 1px solid var(--line); border-radius: 5px; padding: .1em .35em; }
pre code { background: none; border: 0; padding: 0; color: inherit; font-size: 1em; }
button {
padding: .7rem 1.3rem; font-size: 1rem; font-weight: 600; cursor: pointer;
background: var(--accent); color: var(--accent-fg); border: 0; border-radius: 8px;
}
button:disabled { opacity: .6; cursor: default; }
table { width: 100%; border-collapse: collapse; font-size: .92rem; margin-top: 1rem; }
th, td { text-align: left; padding: .55rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
pre { background: #0f172a; color: #e2e8f0; padding: .9rem 1rem; border-radius: 8px;
overflow-x: auto; font-size: .9rem; line-height: 1.5; }
.cta-row { display: flex; gap: .6rem; flex-wrap: wrap; margin-top: 1.1rem; }
.btn { display: inline-block; padding: .55rem 1rem; border-radius: 8px; font-weight: 600;
text-decoration: none; background: var(--accent); color: #fff; }
.btn-secondary { background: transparent; color: inherit; border: 1px solid #cbd5e1; }
.muted-note { color: var(--muted); font-size: .93rem; margin-top: 1.1rem; }
footer { border-top: 1px solid var(--line); margin-top: 4rem; padding: 1.5rem 1.25rem 3rem; }
footer div { max-width: 44rem; margin: 0 auto; color: var(--muted); font-size: .82rem; }
a { color: var(--accent); }
</style>
</head>
<body>
<header class="site">
<div class="logo"><svg class="mark" viewBox="0 0 96 96" aria-hidden="true" focusable="false"><rect x="12" y="14" width="72" height="18" rx="5" fill="var(--mark)"/><rect x="12" y="39" width="48" height="18" rx="5" fill="var(--mark)"/><rect x="12" y="64" width="26" height="18" rx="5" fill="var(--accent)"/></svg>foster<span>stack</span></div>
<div class="tag">Infrastructure worth keeping — and kept patched.</div>
</header>
<main>
<h1>Monitor a Gradle build cache with Prometheus and Grafana</h1>
<p class="lede">The cache server exposes Prometheus metrics and a read-only status page. This page lists what is there, a scrape setting, a few queries, and where to get a ready-made Grafana dashboard.</p>

<h2>Metrics need no password</h2>
<p>The <code>/metrics</code> and <code>/healthz</code> paths stay open even when Basic Auth is on, so a Prometheus scraper needs no credentials. The status page, <code>/statusz</code>, sits behind Basic Auth when it is enabled.</p>

<h2>A scrape setting</h2>
<pre><code>scrape_configs:
- job_name: fscache
static_configs:
- targets: [&#x27;cache.example.com:8080&#x27;]</code></pre>
<p>This is a plain example. Use your own address, and HTTPS if you have put the server behind a proxy.</p>

<h2>The metrics</h2>
<p>These are the names a running 0.2.1 server reports:</p>
<table>
<tr><th>Metric</th><th>Type</th><th>What it tells you</th></tr>
<tr><td><code>fscache_cache_hits_total</code></td><td>counter</td><td>Requests answered from the cache</td></tr>
<tr><td><code>fscache_cache_misses_total</code></td><td>counter</td><td>Requests for something not stored</td></tr>
<tr><td><code>fscache_store_bytes</code></td><td>gauge</td><td>How much is stored now</td></tr>
<tr><td><code>fscache_store_entries</code></td><td>gauge</td><td>How many entries are stored</td></tr>
<tr><td><code>fscache_evicted_entries_total</code></td><td>counter</td><td>Entries removed to stay under the size cap</td></tr>
<tr><td><code>fscache_http_requests_total</code></td><td>counter</td><td>Requests, by method and status</td></tr>
<tr><td><code>fscache_http_request_duration_seconds</code></td><td>histogram</td><td>How long requests take</td></tr>
<tr><td><code>fscache_bytes_read_total</code></td><td>counter</td><td>Bytes sent to clients</td></tr>
<tr><td><code>fscache_bytes_written_total</code></td><td>counter</td><td>Bytes received from clients</td></tr>
</table>

<h2>Queries worth having</h2>
<pre><code># hit rate over the last 5 minutes
sum(rate(fscache_cache_hits_total[5m]))
/ clamp_min(sum(rate(fscache_cache_hits_total[5m])) + sum(rate(fscache_cache_misses_total[5m])), 0.0001)

# entries removed per second to stay under the cap
sum(rate(fscache_evicted_entries_total[5m]))

# 95th percentile request time
histogram_quantile(0.95, sum by (le, method) (rate(fscache_http_request_duration_seconds_bucket[5m])))</code></pre>
<p>A hit rate that never climbs, together with constant evictions, usually means the size cap is too small. See <a href="/build-cache-server-sizing/">server sizing</a>.</p>

<h2>A ready-made Grafana dashboard</h2>
<p>The repository includes a dashboard with eight panels: hit rate, cache size against the cap, entries, evictions, hits and misses, requests by status, throughput, and request latency. <a href="https://github.com/fosterstack/cache/blob/main/docs/grafana-dashboard.json">Download <code>grafana-dashboard.json</code></a> and import it in Grafana.</p>

<h2>The status page</h2>
<p>For a quick look without Prometheus, <code>/statusz</code> returns the same numbers in one place, in a browser or as JSON. This is what a freshly started 0.2.1 server returned after one stored entry and two requests:</p>
<pre><code>$ curl -s localhost:8080/statusz
{
&quot;version&quot;: &quot;v0.2.1&quot;,
&quot;revision&quot;: &quot;0bfc498ecfca3e38cb4a456acda44359a1620c5a&quot;,
&quot;fips140&quot;: false,
&quot;fips140_note&quot;: &quot;off&quot;,
&quot;uptime_seconds&quot;: 2.118684875,
&quot;uptime&quot;: &quot;2s&quot;,
&quot;store_bytes&quot;: 5,
&quot;max_bytes&quot;: 1073741824,
&quot;store_entries&quot;: 1,
&quot;cache_hits&quot;: 1,
&quot;cache_misses&quot;: 1,
&quot;hit_ratio&quot;: 0.5,
&quot;evicted_entries&quot;: 0,
&quot;auth_enabled&quot;: false
}</code></pre>

<p class="muted-note">Related: <a href="/gradle-remote-cache-not-working/">Gradle remote build cache not working</a> and <a href="/gradle-build-cache-docker-compose/">Gradle build cache with Docker Compose</a>.</p>
<div class="cta-row">
<a class="btn" href="https://github.com/fosterstack/cache/blob/main/docs/grafana-dashboard.json">Grafana dashboard</a>
<a class="btn btn-secondary" href="/">FosterStack home</a>
</div>
</main>
<footer>
<div>
<p>FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc.
"Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify
compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation
of the documented remote build cache HTTP protocol. Apache Maven is a trademark of the
Apache Software Foundation; the Maven Build Cache Extension is Apache's project, not
ours.</p>
<p style="margin-top:.6rem">© 2026 FosterStack · <a href="/regulated/">For regulated markets</a> · <a href="https://github.com/fosterstack/cache">github.com/fosterstack/cache</a> · <a href="mailto:hello@fosterstack.com">hello@fosterstack.com</a></p>
</div>
</footer>
</body>
</html>
4 changes: 4 additions & 0 deletions sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://fosterstack.com/monitor-build-cache-prometheus-grafana/</loc>
<lastmod>2026-10-08</lastmod>
</url>
</urlset>
Loading