Can I Restart the Cache Server While Builds Run?
Yes. In our runs, a build that was already running finished normally when we stopped and restarted FosterStack Cache. It printed at most one warning and stopped using the cache for the rest of that build.
The short answer
- If the build touched the cache while the server was down, Gradle printed one warning and switched the remote cache off for the rest of that build. The build still ended with
BUILD SUCCESSFUL. - If it did not, the build printed nothing. That was the case in our 2-second restarts.
- The next build used the cache again and rebuilt only what had not been stored.
- In our runs, a normal stop waited up to 10 seconds for an upload in progress. A longer upload was lost. So was an upload cut off by a kill.
- The stored results came back after a normal stop and after a kill, as long as the server started on the same data folder.
What to do
Our advice, not a test result:
- Stop the server with a normal stop, not a kill.
docker stopsends a normal stop first, per Docker's documentation. We started the server binary directly and did not test Docker. - Restart in a quiet moment. The server was back in well under a second in our runs, so a quiet moment keeps the chance of a warning small.
- Do not worry about one warning. The lines to expect are
Could not store entryorCould not load entry, thenThe remote build cache was disabled during the build due to errors. The next build stores what was missed. - Keep the same data folder or volume. That is what keeps the stored results. See does the build cache survive a restart, below.
- For an upgrade, pull the new version first, then stop and start, so the gap is only the restart. What happens to the data across releases is on upgrade a build cache server.
- Check that the server answers before you send builds back. The
/healthzaddress returns200when it is up. See is my build cache server healthy.
If the server is down for a whole build, nothing breaks either. That case is on what happens when the cache server is down.
Does the build cache survive a restart?
You restart the cache server, or it crashes, or the container is replaced. Do your stored build results come back? We tested a clean stop, a forced kill and a wrong data folder.
- After a clean stop and start on the same data folder, every result was still there.
- After the server was killed with
kill -9and started again on the same folder, every result was still there. - On a different, empty data folder, the cache was empty and the next build rebuilt everything.
The results live in the data folder, not in the running server. If that folder survives, your cache survives.
If your cache comes back empty, the server is looking at a different folder than before. Check that the data folder or volume is still the one you expect:
- Docker: use a named volume or a mounted folder for the data directory. See Gradle build cache with Docker Compose.
- Kubernetes: the data directory needs a persistent volume claim. See Gradle build cache on Kubernetes.
- Permissions: if the server cannot write to the folder, see build cache server won't start: errors and fixes.
To copy the data somewhere safe, or to move it to another server, see upgrade, back up and restore.
How we checked
We ran the Gradle builds on this page twice on a Mac, once with Java 21 and once with Java 27, with the same results. A third run on Linux is in the last section.
Restarts during a build. FosterStack Cache 0.2.1 on a Mac (arm64), no password. Gradle 9.8.0, one fresh copy of the project per build, no reused daemon, the local build cache off. To keep builds open long enough to overlap a restart, each of the four modules had a cacheable task that sleeps for 8 seconds. We sent the signal to the server's process ID and started it again on the same data folder. All builds ended with BUILD SUCCESSFUL. Each row is one run; we did not repeat them.
| Server stopped during the build | Back after | What the build printed | Entries stored | Next build, from cache |
|---|---|---|---|---|
| Normal stop | 2 seconds | Nothing about the cache | 4 of 4 | 4 of 4 |
| Kill | 2 seconds | Nothing about the cache | 4 of 4 | 4 of 4 |
| Normal stop | 14 seconds | One Could not store entry, then the remote cache was disabled | 2 of 4 | 2 of 4 |
| Kill | 14 seconds | One Could not store entry, then the remote cache was disabled | 2 of 4 | 2 of 4 |
We also started a build while the server was stopped. It printed one Could not load entry warning, then built all four modules locally. After we started the server, the next build built and stored all four. The one after that took all four from the cache.
How long the server was unreachable. We stopped and started it five times in a row for each case, and polled /healthz until it answered. This measures the restart on a busy laptop, not a promise for your server.
| Data in the cache | Normal stop, then start | Kill, then start |
|---|---|---|
| 20 small entries | 0.03 to 0.05 seconds | 0.03 seconds |
| 5,000 entries, about 49 MiB | 0.03 to 0.06 seconds | 0.05 to 0.15 seconds |
After a kill, the server logs unclean shutdown detected, reconciling stores before serving and checks its files before it answers. No entries were lost in these runs.
An upload in progress when the server stops. To catch an upload in progress we used curl to send a large file slowly (2 MiB per second) and stopped the server 3 seconds in.
| Upload | Stop | What happened | Entry afterwards |
|---|---|---|---|
| 20 MiB | Normal stop | The server waited, the upload finished with 201, the server exited cleanly after 7.4 seconds | Readable |
| 20 MiB | Kill | The upload broke. A 6 MiB temporary file stayed behind | Not found (404). The next start removed the temporary file |
| 40 MiB | Normal stop | The server gave up after 10.0 seconds and exited with an error. The upload broke. A 26 MiB temporary file stayed on disk | Not found (404) |
In both 20 MiB cases, an entry that was stored earlier stayed readable. We did not test whether a later start removes the temporary file left by the 40 MiB case.
What survives a restart. A four-module Gradle 9.8.0 project (the one from Gradle build cache for a monorepo) against a server set up with FSCACHE_DATA_DIR pointing at a folder, on FosterStack Cache 0.2.1 (macOS, arm64). Every build used a fresh copy of the project and an empty Gradle home, so the only shared thing was the server. Our first run used a project folder that still held build output from an earlier check; we repeated all four steps from a clean copy and got the same results. All four builds ended with BUILD SUCCESSFUL. "From cache" counts the four compileJava tasks that printed FROM-CACHE.
| Step | Server state before the build | Entries in the status page | From cache |
|---|---|---|---|
| 1. First build | New, empty data folder A | 0, then 13 afterwards | 0 of 4 |
| 2. Clean restart | Stopped normally, started on folder A | 13 (same as before) | 4 of 4 |
| 3. Crash | Killed with kill -9, started on folder A | 13 (same as before) | 4 of 4 |
| 4. Wrong folder | Started on a different empty folder B | 0 | 0 of 4 |
The status page is the server's /statusz address. It showed the same 13 entries and the same size in bytes right after each restart, before any build ran.
In this test we killed the server while it was idle. Kills in the middle of an upload are in the upload table above.
Not tested: Docker, Kubernetes or Compose rollouts, Maven builds, releases other than 0.2.1 and 0.2.2, and more than one build running at once.
Second run: Linux, 0.2.2
The runs above were on a Mac with 0.2.1. We ran the same steps again on a GitHub-hosted Linux machine with FosterStack Cache 0.2.2, Gradle 9.8.0 and Java 21. In the build tests we stopped the server while the build's third 8-second task was running, 2 seconds after the second one finished.
- The four rows of the first table came out the same. Back after 2 seconds: no cache message, and the next build took 4 of 4 from the cache. Back after 14 seconds: one
Could not store entry, the remote cache was then disabled, and the next build took 2 of 4. A normal stop andkill -9gave the same results. - A build started while the server was stopped printed one
Could not load entrywarning, disabled the remote cache and built all four modules locally. - The next build, with the server back, built and stored all four. The one after that took all four from the cache.
- Every restart we timed took 0.01 to 0.07 seconds, with 20 entries and with 5,000 entries (about 48 MiB).
- After a kill the log said
unclean shutdown detected, reconciling stores before serving, and after five kills the 5,000 entries were all still there. - We sent uploads at 2 MiB per second and stopped the server after 3 seconds. A 20 MiB upload with a normal stop finished with
201. The server exited with code 0 after 7.3 seconds. - A 20 MiB upload with a kill broke. A 6 MiB temporary file stayed behind, the entry was not found (404), and the next start removed the file.
- A 40 MiB upload with a normal stop broke when the server gave up at 10.0 seconds and exited with code 1. A 26 MiB temporary file stayed on disk and the entry was not found. An entry stored earlier stayed readable in all three cases.
- After a clean stop and after a kill, the status page showed the same entries and bytes (11 entries on this test project; the Mac run had 13) and the next build took everything from the cache. A build using a different, empty cache folder took none from the cache.