Skip to content

feat(arc): add a node-cache capability that shares a node directory between a profile's pods - #90

Merged
Mearman merged 1 commit into
mainfrom
feat/arc-node-cache-capability
Oct 14, 2026
Merged

Mearman merged 1 commit into
mainfrom
feat/arc-node-cache-capability

Conversation

@Mearman

@Mearman Mearman commented Oct 14, 2026

Copy link
Copy Markdown
Member

Adds a fourth capability kind, node_cache, for #83. It gives a profile's runner pods on a node one shared directory of that node, so a package manager's store survives the pod and a job on a warm node starts with it filled.

The runner pod gets a hostPath volume at <host path>/<namespace>/<release>, mounted once on the runner container. An init container prepares the subdirectories the capability names as the runner's user, and each dirs variable is set to its subdirectory's path, so pnpm_config_store_dir: pnpm puts pnpm's store in /node-cache/pnpm. The workflow's own cache step stays the fallback, since a workflow only uses the path when it exists. A pruner DaemonSet (from the autoscaler image) empties a profile's whole cache when it passes github_runner_arc_node_cache_size_limit_gib, and only while no runner pod of that profile is running on the node; it never trims a cache, because a running job may be linked into any of it.

The issue suggested getting hard links by putting the work directory on the same volume as the store. I tested that on a Linux host: link(2) fails with EXDEV between two bind mounts of one directory and between a directory and a bind mount of one of its subdirectories (which is what subPath is), and only works inside a single mount. The runner's workspace path is fixed and a workspace shared by pods would collide on _temp and _actions, so the workspace cannot live on the shared mount. The design therefore expects a package manager that links with symlinks: with pnpm's global virtual store, a project's node_modules holds symlinks into <store>/v11/links plus a few metadata files, and a second project installing the same dependencies writes no package files (checked with a real install against a shared store directory).

The cache is shared writable state, so the capability is opt-in per profile and the README says not to give it to a profile that runs fork pull requests. Cluster Pod Security levels that refuse hostPath will refuse it too.

…etween a profile's pods

A node-cache capability mounts one hostPath directory per profile on the runner container and
prepares the subdirectories it names as the runner's user, setting a variable for each to its
path, so a package manager's content-addressed store survives the pod and the next job starts
with it filled. A pruner DaemonSet empties a profile's cache when it outgrows its limit and no
runner pod of the profile runs on the node.
@chatgpt-codex-connector

Copy link
Copy Markdown

This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins.

@Mearman
Mearman merged commit 39c55dc into main Oct 14, 2026
35 checks passed
@Mearman
Mearman deleted the feat/arc-node-cache-capability branch October 14, 2026 23:01
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.31.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant