Cache and artifacts
The runner serves the GitHub Actions cache and artifacts v4 to jobs. Both use one channel per sandbox.
Channel
- The services are reachable only from inside the sandbox.
- They listen on the sandbox loopback (
127.0.0.1:9089) and on the address of its own Docker bridge (docker0, typically172.17.0.1:9089). - Nothing listens on a host network or port. The host firewall needs no rule.
- A relay in the sandbox pipes each connection to the host over a channel that belongs to that sandbox:
vm: vsock port 9089.container:cache.sockin the per-job directory.
- A sandbox cannot reach another sandbox's channel.
- Limits: 32 connections per sandbox, 128 in all. A transfer with no progress for 60 s is cut.
Variables
- Node actions get
ACTIONS_CACHE_URL,ACTIONS_RUNTIME_URL,ACTIONS_RUNTIME_TOKEN,ACTIONS_CACHE_MODEandACTIONS_RESULTS_URL.run:steps get none, as on the official runner. - In job containers and docker actions the same variables point to the Docker bridge address.
- The token is masked.
- If the relay cannot start, the job runs without a cache.
Cache (API v1)
Works with actions/cache, setup-go and setup-node with cache:, and anything built on @actions/cache. The toolkit uses cache API v1 for any server that is not github.com, *.ghe.com or *.localhost. ACTIONS_CACHE_SERVICE_V2 is never set.
| Setting | Value |
|---|---|
| Storage | <state dir>/cache, mode 0700/0600, written atomically |
| Size cap | ERMITE_RUNNER_CACHE_MAX, default 10G. Least recently used entries are evicted |
| Off | ERMITE_RUNNER_CACHE=0 |
| Restart | Entries are kept. Unfinished uploads are dropped |
- Scopes. Entries belong to (repository, ref, key, version). A job writes to its write ref and reads its read refs in priority order. Another repository's entries are never visible.
- Server scope. The server sends the repository, write ref and read refs (feature
ci.cache). The runner checks them: the repository must be the job's own, refs are fullrefs/...names, at most 8 read refs. An empty write ref makes the job read-only. A scope that fails the checks disables the cache for that job. - Old servers. Without a scope from the server, the scope is
github.repositoryand the job ref, for reading and writing. - Duplicates. A second reservation of the same entry while one exists is a
409. - Reservations. Dropped after an hour without activity, or at the expiry of the token that made them.
- Limits. Each entry is charged at least 4 KiB. At most 256 uploads in progress (32 per job). An entry being downloaded is never evicted. A reservation that cannot get room answers
503. - Bandwidth. A VM's cache traffic does not cross
eth0. The runner limits it per sandbox atERMITE_RUNNER_JOB_NET_BANDWIDTH. Containers have no limit.
Tokens
ACTIONS_RUNTIME_TOKENis a JWT (HS256) bound to the job, repository, refs and an expiry (job timeout plus 15 min).- The key is random, made at start and kept in memory only.
- Any invalid token gets a bare
403. - The token is not an ermite credential. The server refuses it.
- Archive URLs carry their own HMAC and are served only to the job they were issued to.
Action archives
- The host downloads an action archive once into
<state dir>/actions-cache/, keyed by forge, repository and commit sha. - The sandbox gets a signed URL on the cache channel and extracts the archive.
- The SHA-256 is checked on every read. A corrupt entry is dropped and fetched again.
- Size cap:
ERMITE_RUNNER_ACTIONS_CACHE_MAX, default2G.0turns it off. - The sandbox never writes to this cache. Anything that cannot be cached is downloaded by the sandbox.
Artifacts (v4)
actions/upload-artifact@v4 and later, actions/download-artifact@v4 and later, and anything bundling @actions/artifact v2 work when the server has the feature ci.artifacts.
- ermite stores the artifacts. The runner keeps nothing on disk.
- The runner serves GitHub's results service (Twirp JSON plus Azure block blob requests on signed URLs) and translates it to the server with the job token.
- The job token never enters the sandbox.
- A job reaches only the artifacts of its own run.
- Quota and size limits answer
412(413on a block) and are not retried. Server unavailable answers503, which the toolkit retries. - Off:
ERMITE_RUNNER_ARTIFACTS=0or a server withoutci.artifacts. Artifacts need the cache service, soERMITE_RUNNER_CACHE=0turns them off too.
GITHUB_SERVER_URL in artifact steps
@actions/artifactrefuses to run on GitHub Enterprise Server, which it detects only fromGITHUB_SERVER_URL.- In node steps of
actions/upload-artifactandactions/download-artifact(any subpath), and of actions that contain the toolkit's GHES message,GITHUB_SERVER_URLishttps://ermite.ghe.localhost. - Nothing resolves or connects to that host.
- The
githubcontext,${{ github.server_url }}and all other steps keep the real URL.
Not covered
upload-artifactbelow v4.3.1 anddownload-artifactbelow v4.1.2.- Artifacts v3.
- The
artifact-urloutput. It points at the fake host. The run page in ermite lists the artifacts. - Downloads from another run (
run-idwithgithub-token).