Repository navigation
feat(runner): persistent mise/hk caches, nightly pruner, explicit ephemeral, image identity - #24
Merged
Merged
Conversation
…ly pruner script cleanup.sh no longer scans ~/.gradle and ~/.bun on every job (21-39 s, see #16); the shallow gradle lock find and a top-down /tmp sweep replace the full-tree walks. pruner.sh does LRU caps by atime, whole-version mise eviction and docker prune. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ION identity Set MISE_DATA_DIR, MISE_CACHE_DIR, HK_CACHE_DIR and HK_PKL_CACHE_DIR to dirs that compose mounts as volumes. Bake build args into ENV, wired from deploy.yml (metadata tag, github.sha) and build.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t ephemeral runners Add mise-data, mise-cache, hk, rustup (#8) and konan volumes per runner, a pruner service (#16), a Runner.Listener healthcheck, EPHEMERAL=true (the upstream entrypoint only checks non-empty, so 'false' always enabled it) and a beelink LABELS default. rpi5 override drops heavy caches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add hosts/{hetzner,beelink,rpi5}.env.example with the target labels, update .env.example
(any non-empty DISABLE_AUTO_UPDATE disables updates) and the README cache/label/ephemeral docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve image identity ARG last for layer cache Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Stream S2 of the CI redesign: runner image changes. Targets this fork's
main(not upstream).Refs #16, #8. Partially addresses #7 (see below).
Changes
MISE_DATA_DIR=/root/.local/share/mise,MISE_CACHE_DIR=/root/.cache/mise,HK_CACHE_DIR=/root/.cache/hkandHK_PKL_CACHE_DIR=/root/.cache/hk/pklr(hk's real env vars per the hk docs; pinning the pkl cache underHK_CACHE_DIRkeeps it on the same volume). One named volume per service per directory.~/.cacheis never mounted as a whole, so it can't shadow the pnpm volume.~/.rustupand~/.konanvolumes per service (No ~/.rustup volume: the Rust toolchain reinstalls on every X64 job #8).du -sb+ fullfindover ~20 GB caches on each job-completed hook #16). Thelru_capscans leavecleanup.sh. A newprunercompose service (same image,pruner.shentrypoint,docker.sockplus only the capped cache volumes) runs daily: LRU caps on gradle, bun, mise cache and hk (ordered by atime, because Gradle never touches mtime on read), whole-tool/versioneviction for mise installs,docker system prune -af --filter until=24h,docker builder prune -af --filter until=72h. Named volumes are never pruned.cleanup.shkeeps the cheap work:/tmpsweep, Gradle daemon/journal/locks,_diag. I also made two of its own walks cheap: the/tmpsweep no longer descends into the spared workdir (find -depthdisables prune), and the Gradle lockfindis limited tocaches/at depth 3.ARG/ENV RUNNER_IMAGE_TAGandRUNNER_IMAGE_REVISION, set bydeploy.yml(docker/metadata-action pinned tag,github.sha) andbuild.yml(pr-<n>, head SHA). The ARGs sit late in the Dockerfile so they don't bust the SDK layers.pgrep -f Runner.Listenerin exec form (as in closed feat: make the three-runner compose a Coolify service #10; under CMD-SHELLpgrep -fmatches its ownsh -c).hosts/{hetzner,beelink,rpi5}.env.examplewith the target labels,hosts/rpi5.compose.yml,LABELSdefault, README cache/label tables and rollout.The ephemeral finding
I read
/entrypoint.shfrommyoung34/github-runner:2.338.0-ubuntu-noble: both flags areif [ -n "${EPHEMERAL}" ]/if [ -n "${DISABLE_AUTO_UPDATE}" ], so any non-empty value enables them.EPHEMERAL: "false"always passed--ephemeral: these runners have always been one-job-per-container, restarted byrestart: unless-stopped. This PR setsEPHEMERAL: "true"with a comment, which changes no behaviour. Caches persist because they live on named volumes, not in the container.DISABLE_AUTO_UPDATEnow defaults to empty (updates on);.env.exampledocuments that any non-empty value, including "false", disables updates. (#10 took the opposite view, that omittingEPHEMERALmakes runners persistent; that is true of an unset variable but not of the"false"that was shipped.)Expected per-job saving
Issue #16 measured the
Complete runnerhook at 11-41 s per job (median ~25 s), with the post-job disk-free unchanged. After this PR the hook does only a shallow/tmpsweep and a depth-limited Gradlefind; expect roughly 1-3 s. That is about 20-35 s per self-hosted job, ~4-5 runner-hours a week fleet-wide. Persistent mise/hk caches additionally avoid re-downloading tools on every fresh container; I did not measure that.Host rollout
Runners are ephemeral and re-register on their next job. On each host:
.envfromhosts/<host>.env.example(keep realAPP_*values), thendocker compose pull && docker compose up -d.LABELS=self-hosted,Linux,X64,docker,x86,light,agent,hetzner(addsagent).LABELS=self-hosted,Linux,X64,docker,x86,large,light,beelink(addslight).LABELS=self-hosted,Linux,ARM64,docker,agent,rpi5(dropslight, addsagent) andCOMPOSE_FILE=docker-compose.yml:hosts/rpi5.compose.ymlso no heavy cache volumes are mounted.docker compose psshowshealthy, and the runner list shows the new labels.New cache volumes start empty and warm on first jobs. Existing
runner-work*volumes are untouched.jdx/mise-actionshould usecache: falseon these runners.Not done / notes
pre-job.sh) is out of scope.Verification
docker compose configvalid for the base file and with-f hosts/rpi5.compose.yml(rpi5 runners then have onlydocker.sockand work; pruner has onlydocker.sock).bash -nandshellcheckclean oncleanup.sh,pruner.sh,pre-job.sh.RUNNER_IMAGE_TAG,RUNNER_IMAGE_REVISION,MISE_*,HK_*present in the image.pruner.shandcleanup.shrun inside the image against fixture dirs: LRU cap evicted the oldest file and the oldest mise tool version;/tmpsweep kept only the workdir tree.🤖 Generated with Claude Code