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
141 changes: 141 additions & 0 deletions gradle-build-cache-docker-compose/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gradle build cache with Docker Compose</title>
<meta name="description" content="A shared Gradle remote build cache in one Docker Compose file: password, size limit, data volume, a curl check that needs no Gradle project, and health checks.">
<link rel="canonical" href="https://fosterstack.com/gradle-build-cache-docker-compose/">
<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>Gradle build cache with Docker Compose</h1>
<p class="lede">Run a shared Gradle build cache for your team in one Compose file, with a password and a size limit. This is the shape we suggest once you go past a first trial.</p>

<h2>Try it first, on one machine</h2>
<pre><code>docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot ghcr.io/fosterstack/cache:latest</code></pre>
<p>The <code>127.0.0.1:</code> keeps it reachable from this machine only. A bare <code>-p 8080:8080</code> opens the port to every network interface, and on a cloud host that is a cache anyone can write to the moment the command returns. Pin a version tag instead of <code>latest</code> for anything real.</p>

<h2>The Compose file</h2>
<p>This one is open to your CI runners, so it has a password on purpose. A cache that untrusted people can write to can feed files into your builds.</p>
<pre><code>services:
fscache:
image: ghcr.io/fosterstack/cache:latest # pin a version tag
restart: unless-stopped
ports:
- "8080:8080" # all interfaces, so auth is on
volumes:
- fscache-data:/home/nonroot
environment:
FSCACHE_MAX_BYTES: "53687091200" # 50 GiB, size to your CI volume
FSCACHE_USERNAME: gradle
FSCACHE_PASSWORD: change-me # generate one: openssl rand -base64 24

volumes:
fscache-data:</code></pre>
<ul class="trust">
<li>The data volume mounts at <code>/home/nonroot</code>, not <code>/data</code>. The server runs as a non-root user.</li>
<li><code>FSCACHE_MAX_BYTES</code> is in bytes. 50 GiB is <code>53687091200</code>. See <a href="/build-cache-server-sizing/">server sizing</a> for more values.</li>
<li>Put an HTTPS proxy in front for real use. Gradle sends the password with every request.</li>
</ul>

<h2>Check it works, with no Gradle project</h2>
<pre><code>curl -s localhost:8080/healthz # ok
curl -s -X PUT --data-binary 'hello' localhost:8080/testkey123 # 201
curl -s localhost:8080/testkey123 # hello</code></pre>
<p>Add <code>-u gradle:your-password</code> to those calls once you have set a password.</p>

<h2>Point Gradle at it</h2>
<p>Use the settings from our <a href="/migrate-build-cache-node/">Gradle and Maven setup</a> page, and turn caching on with <code>org.gradle.caching=true</code>. If nothing comes from the cache, work through <a href="/gradle-remote-cache-not-working/">Gradle remote build cache not working</a>.</p>

<h2>Health checks</h2>
<p>The container image has no shell, so there is no health check inside the Compose file. Probe <code>/healthz</code> from outside: your orchestrator's HTTP check, a monitor, or <code>curl -f http://localhost:8080/healthz</code> from the host.</p>

<p class="muted-note">Full detail: <a href="https://github.com/fosterstack/cache/blob/main/docs/docker-deploy.md">Docker deploy guide</a>.</p>
<div class="cta-row">
<a class="btn" href="/migrate-build-cache-node/">Gradle and Maven setup</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 @@ -16,4 +16,8 @@
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://fosterstack.com/gradle-build-cache-docker-compose/</loc>
<lastmod>2026-10-08</lastmod>
</url>
</urlset>
Loading