fix(forgejo): recycle runner scratch by label, not a name list #770
No reviewers
Labels
No labels
burndown-2026-06
burndown-2026-08
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/infrastructure!770
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/runner-recycle-selector"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Replaces #769, which diagnosed the same incident correctly but paid too much for it.
kai-server root filesystem hit 89.6 percent because the scoped deploy and publisher runner pools were never in the recycle inventory. They carry the same 24Gi
emptyDirdocker-libscratch and the same start-time wipe as the general pools, so their scratch grew for the whole pod lifetime. Measured from the kubelet summary API: 96.44 GiB across 14 pods that had not restarted in 8 days, roughly 24 percent of everything on the disk, all of it disposable.What
Declare the contract once and select on it, instead of enumerating pools.
coilyco.io/runner-scratch: recycleon its StatefulSetmetadata.labels. Scoped overlays inherit it throughnameSuffix.rollout restart statefulset --selector coilyco.io/runner-scratch=recycle.RoletradesresourceNamesfor namespacedlist.A new deploy, publisher, or general pool is recycled the day it lands, with no edit here.
Why not #769
That change added 13 hand-copied 15-line container blocks and 13
resourceNames. Its body says targets become derived, but the derivation lives only in the test, which then enforces the hand-maintained list. Net effect after merge:rancher/kubectldigest at 21 copies across two filesThis branch instead: 97 lines off the recycle manifest, 89 off the ser8 overlay, digest appears once, zero names anywhere.
The list also tracked an inventory the
coilyco-bridge/deployrepo owns. Under the config-placement rule, infra should not hold a copy of it. The selector removes the copy without moving the mechanism, which correctly stays in infra.Coverage
Identical in effect to #769. Verified by rendering
deploy/_flux/appsand both ser8 roots:forgejo-runner-tap-writermounts no scratch and stays unlabelledRendered diff against
mainis exactly three things: the RBAC verb change, 17 label insertions, and the container list collapsing 4 to 1. No runnerspecis otherwise touched, and nospec.selectorchanges.RBAC tradeoff
This is the one place #769 was stronger, so stating it plainly. Kubernetes cannot scope a
listby name, and a selector needslist, soresourceNameshad to go. The Role stays namespaced toforgejo, whose only other StatefulSet isforgejo-db. Nothing but this CronJob binds the ServiceAccount, the image is distroless with no shell, and the argument vector is fixed at apply time, so the widened verb set is not reachable as a runtime choice.Test
The recycle test now asserts the contract rather than a copy of the list:
docker-libscratch and the recycle label must imply each other in both directions, across the rendered scoped aggregate and both ser8 roots. That catches the drift the old list could not, a runner gaining scratch without the label, and the inverse, a labelled runner restarted for nothing.Verification
ward exec forgejo-runner-recycle-teststatic stage passes:forgejo runner recycle manifest: okpre-commit run --all-filespassesExec format erroron the amd64 kubectl. Same failure as unmodifiedmain, invalidate_image_entrypoint, which this diff does not touch. CI on the amd64 runner exercises it.Not included
forgejo-runner-deploy-sirens-discord-opsholds ~23 GB and has no manifest in any repo. It carries Fluxappslabels, so it was applied once and its source was removed without a prune. Unlabelled here, so still unrecycled. Needs a separate retirement decision, same conclusion #769 reached.sizeLimitis 336 GiB permitted on a 479.55 GiB filesystem. Nightly recycle bounds the typical case, not a bad day.Kai reclaimed the 96 GiB by hand before this branch, so the immediate pressure is already resolved. This makes it not recur.