Help us shape the future of Go! Take the 2026 Go Developer Survey

ci-support

module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT

README

ci-support

The CI services every GitHub Actions runner uses (caches and docker builders), independent of how the runner is hosted.

Part Where What it is
ci-builders chart charts/ci-builders the Helm chart that deploys the services below
cache-redirect image/cache-redirect a pull-through cache for Go modules, Nix, Maven, Gradle and npm that answers a hit with a redirect to a presigned URL of its bucket
Builder trust the chart's buildkitd.mtls.clientCA, externalBuilders the client CA, the client certificates of runners outside the cluster, and the builders' server certificates pushed to a store (see docs/mtls.md)
Other caches the chart's bazelRemote, npmCache bazel-remote and the npm read-through cache
Docker builders pkg/buildkit, pkg/buildkit/ec2 Go libraries: the BuildKit daemon rendered for a machine of its own (provider-neutral), and Pulumi components that run it as one on-demand EC2 instance per architecture behind a wake proxy on a shared network load balancer, with truvity/asg

The chart and cache-redirect moved here from truvity/ci-plane. One v* tag publishes the image at ghcr.io/truvity/ci-support/cache-redirect and the chart at oci://ghcr.io/truvity/ci-support/charts/ci-builders, with every image digest stamped into the chart defaults.

Who it is for

Anyone who runs GitHub Actions runners and wants the services around them to outlive the runner technology. The services here sit beside the runners, not in them: a runner on a Kubernetes cluster, on a serverless container platform or on bare metal talks to the same caches and builders over the network.

It assumes a Kubernetes cluster to run the services on. It deliberately does not install the runners themselves (see Neighbours), and it holds no estate values: account ids, hostnames, registries and regions are inputs supplied by the consumer.

The model

Three nouns:

  • Cache: a service a build reads from and writes to so that repeated work is paid for once (compiled Bazel actions, npm packages, Go modules and Nix store paths).
  • Builder: a service that does the build work on behalf of a runner, so the runner needs no privileged container runtime (a docker builder, a Nix worker).
  • Runner: not in this repository. Whatever executes the workflow job, reaching the caches and builders as a client.

The services are indifferent to which runner calls them; that independence is the reason for the split from the runner images.

Install and a worked example

Install the chart from the registry with an explicit --version and a values file that names your own cluster facts. The chart refuses to render without the ones it requires (for example the namespaces allowed to reach each cache):

helm install ci-builders oci://ghcr.io/truvity/ci-support/charts/ci-builders \
  --version <X.Y.Z> -n <namespace> \
  --set npmCache.networkPolicy.consumerNamespaces={<runner namespace>}

docs/day-1-install.md walks through a fresh install, docs/mtls.md the builders' mutual TLS.

The builders on EC2 are a Go library, pinned in the consumer's go.mod:

go get github.com/truvity/ci-support@v1.0.0
proxy, err := ec2.NewProxy(ctx, "buildkit-proxy", &ec2.ProxyArgs{
    Name: "<env>-buildkit-proxy", Region: region, VPCID: vpc, SubnetIDs: private,
    NLB: sharedNLB, Sources: []front.Source{{SecurityGroupID: runnerSG}},
    Routes: []ec2.ProxyRoute{{Name: "arm64", Port: 1235, TargetTagValue: "<env>-buildkit-arm64"}},
})

b, err := ec2.New(ctx, "buildkit-arm64", &ec2.Args{
    Arch: "arm64", Name: "<env>-buildkit-arm64", InstanceType: "m7g.xlarge",
    VPCID: vpc, PermissionsBoundary: boundaryARN,
    Port: 1235, // from the consumer's port registry
    Certificate: ec2.Certificate{
        Cert: "/<prefix>/arm64/cert", Key: "/<prefix>/arm64/key", ClientCA: "/<prefix>/client-ca",
        KeyAlias: "alias/<key of those parameters>",
    },
    Daemon: buildkit.Config{MaxParallelism: 6},
    OnDemand: &ec2.OnDemandArgs{
        SubnetID: subnet, Zone: "<zone>", ProxySecurityGroupID: proxy.SecurityGroupID(),
    },
})

docs/buildkit-on-ec2.md describes the machine. Each builder is a single stopped instance with a persistent cache volume, behind an always-on wake proxy (ec2.NewProxy) that starts it when a client connects and stops it after 20 idle minutes. v1.0.0 removed the group mode (an Auto Scaling group with a warm pool per builder); see CHANGELOG.md.

Consumers

Who uses this, and through which surface (2026-10-25):

Consumer Through
GitHub Actions runners on serverless containers, see truvity/grun the cache and builder endpoints
A second, non-AWS estate the ci-builders chart
The builders on EC2, in the consumer's own deploy program pkg/buildkit/ec2

Runner identity (Go, Pulumi)

pkg/runneridentity is the AWS side of the runner pods: the Pod Identity roles for the runner pools, the module proxy and cache-redirect, built with truvity/k8s's pkg/aws/podidentity. Every name is an input; nothing grants cluster access or stable publishing. It moved here from truvity/ci-plane (deploy/pulumi/runneridentity) with the same API; see the package documentation.

Neighbours

Documentation

The rules for working in this repository are in CONTRIBUTING.md, the reporting policy in SECURITY.md and the change history in CHANGELOG.md.

The rule that makes this repository public

Mechanism only. Account ids, ARNs, hostnames, registries, bucket names and organisation-specific configuration are inputs supplied by the consumer and live in the consumer's own configuration repository, never here. hack/leak-canary.sh runs in CI on every pull request and fails the build if a particular slips in.

Status

True on 2026-10-26: v0.1.0 is the first release. The ci-builders chart and cache-redirect moved here from ci-plane with byte-identical goldens. pkg/buildkit/ec2 is tested against Pulumi mocks and fake commands; no builder made by it has booted yet, so the first deploy is its first real test.

Development

devbox shell
just check

just check needs no credentials and no cluster. just --list shows the recipes.

Releasing

A v* tag is the release act (.github/workflows/release.yaml); see docs/day-2-operations.md. The same tag versions the Go module. Releases are hand-cut tags; the CI policy-conformance job is a gate. Auto-release is not armed.

Licence

MIT, see LICENSE.

Directories

Path Synopsis
pkg
buildkit
Package buildkit renders a BuildKit daemon for a machine of its own, with no provider in it: the daemon's configuration (buildkitd.toml: garbage collection, parallelism, the mutual-TLS listener, the debug address), its systemd unit with memory limits, the install of a pinned BuildKit release and rootlesskit, an nftables rule that keeps every process but root away from the instance metadata service.
Package buildkit renders a BuildKit daemon for a machine of its own, with no provider in it: the daemon's configuration (buildkitd.toml: garbage collection, parallelism, the mutual-TLS listener, the debug address), its systemd unit with memory limits, the install of a pinned BuildKit release and rootlesskit, an nftables rule that keeps every process but root away from the instance metadata service.
buildkit/ec2
Package ec2 runs a BuildKit builder (package buildkit) on EC2: ONE plain instance per architecture (asg Args.Single) that is stopped when nobody builds, with its cache on a persistent encrypted data volume (asg Args.Data, mounted by the datavolume module at the daemon's state directory), behind a wake proxy (NewProxy, asg's cmd/wake-proxy) that owns its run state: it starts the instance when a client connects, holds the connection until the daemon answers, pipes the TCP stream through (mutual TLS stays end to end) and stops the instance after 20 minutes without a connection.
Package ec2 runs a BuildKit builder (package buildkit) on EC2: ONE plain instance per architecture (asg Args.Single) that is stopped when nobody builds, with its cache on a persistent encrypted data volume (asg Args.Data, mounted by the datavolume module at the daemon's state directory), behind a wake proxy (NewProxy, asg's cmd/wake-proxy) that owns its run state: it starts the instance when a client connects, holds the connection until the daemon answers, pipes the TCP stream through (mutual TLS stays end to end) and stops the instance after 20 minutes without a connection.
runneridentity
Package runneridentity is the AWS side of the CI plane's runner pods: the Pod Identity roles the ARC runner pools, the Go module proxy and cache-redirect assume.
Package runneridentity is the AWS side of the CI plane's runner pods: the Pod Identity roles the ARC runner pools, the Go module proxy and cache-redirect assume.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL