actions/cache, actions/setup-* (cache: npm and friends),
Swatinem/rust-cache, astral-sh/setup-uv and anything else built on
@actions/cache then work unchanged, reading and writing an S3 bucket in
your own account, in the runner’s own region. No size limit beyond your
bucket, and no 10 GB per-repository ceiling.
airship.yaml
Available on Linux runners on AWS. On Google Cloud and Azure,
cache: true is not an error — the runner launches without the proxy and
Events records a cache.unavailable warning — so a shared definition
stays portable.Docker build cache
docker/build-push-action needs the url_v2 form, pointed at the local
proxy:
How it works
1
The bucket is prepared, and proven to be yours
At launch, Airship makes sure
airship-cache-<region>-<hash> exists in
your account: private, public access blocked, entries expired after seven
days, abandoned multipart uploads after one. Every call names the expected
owner account, so a bucket of that name in another account is refused
rather than used.2
The machine gets a proxy and a one-off token
The boot script downloads the cache proxy from the control plane, starts
it, and trusts its certificate authority on that machine only.
3
The proxy trades its token for narrow credentials
An STS federation token derived from your access key, allowed to read and
write only the cache bucket, valid for an hour and refreshed before it
expires. Your access key never reaches the machine.
4
Cache calls are answered locally
The runner’s
https_proxy points at the proxy. It terminates TLS for
exactly one host, answers the cache service’s calls itself — reserve,
upload, finalize, lookup — and forwards everything else on that host to
GitHub untouched. Every other host is tunnelled without inspection.5
Objects move straight to and from S3
Uploads are staged on the root volume and written as a multipart upload
with eight parallel 64 MiB parts, sent from disk. Downloads are served
straight from S3: one GET for
actions/cache, ranged GETs for clients
like buildx.<version>/<key>, and restore-keys match by prefix
within the same version, newest first — the same semantics GitHub’s own cache
has, so a migrated workflow behaves identically.
If anything in that chain fails, the job still runs, without acceleration,
and Events records why.
The IAM permissions
In addition to EC2 access, the connected IAM user needs:Getting the speed
Raise the disk first
gp3 defaults to 125 MiB/s whatever its size, which caps restores well
below what the network does.
Throughput: 1000 with Iops: 6000 is the
starting point.Add an S3 gateway endpoint
It keeps cache traffic off the internet and off your NAT bill, and is
usually the best single network change.
Pick a network-heavy instance class
Once the disk is not the bottleneck, the instance’s network allocation is
the next lever.
Keep cache steps on the host
Steps inside a
container: job inherit https_proxy but not the host’s
trust store, so run cache steps outside the container.Scope, stated plainly
The rest of the cache’s security properties — the local, one-day certificate authority, the narrow interception, the proven bucket ownership — are in Security.When it does not work
On the machine itself,
/var/log/airship-cacheproxy.log has the whole story.