feat: persist per-runner cache volumes with surgical prune - #6
Merged
Conversation
Bun is not in this image — setup-bun installs it per job — but /root/.bun/install/cache survives between jobs in the same container. A job killed mid-download leaves a partial tarball, and every later job on that container then fails with: error: Fail extracting tarball for "next" The failure is sticky and misleading: re-running does not clear it, and it reads as a dependency problem rather than a runner problem. It was previously attributed to the macOS runners specifically, and kodni.me's ci.yml still prescribes pinning to Linux as the fix — but it just reproduced on a Linux X64 beelink. The cause is the shared cache on any persistent runner, not the OS. Also prunes the npm/pnpm/yarn stores, which fail the same way. NOTE: half a fix on its own. Workflows restoring ~/.bun/install/cache via actions/cache with restore-keys can re-import a poisoned entry from the GitHub cache, which outlives this container. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Named volumes only help if cleanup keeps bun/Gradle/Cargo homes and spares RUNNER_WORKDIR; wholesale rm -rf was the wrong policy. Co-authored-by: Cursor <cursoragent@cursor.com>
azlekov
added a commit
to dodi-smart/.github
that referenced
this pull request
Aug 18, 2026
…e Pis Three gaps between this action and the runner image's per-runner cache volumes (dodi-smart/docker-github-actions-runner-android#6): pnpm and yarn were never isolated. The image mounts and LRU-caps /root/.cache/pnpm, and cleanup.sh keeps ~/.cache/yarn, but isolate mode set only npm_config_cache. npm's `cache` is not pnpm's `cache-dir`, so a deps-verify job on pnpm still read and wrote the shared store — it was not verifying. pnpm reads npm_config_* as its own config, so npm_config_store_dir and npm_config_cache_dir pin it; yarn takes YARN_CACHE_FOLDER and YARN_GLOBAL_FOLDER, for classic and berry alike. PUB_CACHE is now job-scoped in every mode, not just when isolated. A home dir is only worth using if a volume backs it. The image mounts one for bun, npm, pnpm, gradle and cargo and caps each at ~20 GB; it mounts none for pub. Left on ~/.pub-cache, a persistent runner would grow it in the container's writable layer, where the post-job LRU cap cannot reach. The forced-isolate check is Linux ARM64, not ARM64. The rule exists because a Pi has 8 GB and must not grow container volumes. The self-hosted Mac is ARM64 too, has no container volumes, and was being made to run every job cold for a reason that does not describe it. Also corrects the bun comment: setup-bun still caches the bun executable, a small exact-keyed binary. What is never uploaded is the install cache — any package STORE under restore-keys is the poison path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rewrite — do not merge the old rm -rf
The previous commit on this branch wiped
/root/.bun/install/cache(and npm/pnpm/yarn) after every job. Combined with cleanup already deleting Gradlebuild-cache-*/transforms-*, that would have thrown away the named volumes at the end of every job.This rewrite lands the beelink layout and keeps the caches warm.
What landed
docker-compose.yml— replaces the one-service stub with the 3-runner X64 beelink compose (YAML anchor + per-runner work and cache volumes). Not a sidecompose.beelink.yml. Volumes are per runner, not shared: concurrentbun installs must not share one tree.pre-job.sh(ACTIONS_RUNNER_HOOK_JOB_STARTED) — surgical prune only: bun*.tmp/*.part, Gradlemodules-2.lock/journal-*/daemon/. Covers OOM / killed runner (JOB_COMPLETED does not run then). Does notrm -rfcache trees.cleanup.sh(ACTIONS_RUNNER_HOOK_JOB_COMPLETED)modules-2/build-cache-*/transforms-*, Cargo registry/tmpcontents except$RUNNER_WORKDIR(and$RUNNER_TEMPif under/tmp) — workdir is/tmp/runner/work, so a blanketfind /tmp -deletewas wiping the work volume_diaglogspre-job.sh, setACTIONS_RUNNER_HOOK_JOB_STARTED, keep JOB_COMPLETED cleanupGRADLE_USER_HOME/BUN_INSTALL_CACHE_DIRon these runners (setup-stacklocal-home mode)The other half
GitHub
actions/cachewithrestore-keyscan still re-import a poisoned bun tarball. That is the shared-workflow / per-repo exact-key work, not this image.Host follow-up (after merge + image publish)
On each beelink, from this compose file (existing
runner-work*volumes keep their data; new cache volumes start empty and warm on first jobs):Do not tmpfs
/tmp. Do not share one bun/gradle volume across the three containers. ARM64 / Pi: no cache volumes.