Package caches
fremforge no longer runs separate shared caches for hosted runners. The cache is your organisation’s own dependency proxy, in the same EU region as the runner VMs. With runner pull-through on, the default for every organisation that has not saved its own dependency proxy settings, each job is pointed at it when it starts (see Hosted runners):
| Traffic | Pointed at your proxy by | Without runner pull-through, or on a fallback |
|---|---|---|
| npm, pnpm (via the job’s npmrc) | the npm registry setting | registry.npmjs.org |
| pip and uv | PIP_INDEX_URL, UV_DEFAULT_INDEX | PyPI |
Go, Maven, NuGet (each when listed in runner_pull_through_ecosystems, all by default) | GOPROXY, a Maven mirror of Central, a machine-wide NuGet.Config | proxy.golang.org, Maven Central, nuget.org |
Trivy vulnerability DB (when trivy is listed) | TRIVY_DB_REPOSITORY, TRIVY_JAVA_DB_REPOSITORY | ghcr.io/aquasecurity |
| Container images | the job’s Docker daemon (see Container images) | the registry the image names |
Every fallback goes to the public registry through fremforge’s EU egress proxy. The proxy is checked at the start of every job; if it cannot be reached, the job uses the public registries and the fallback is logged.
The proxy runs in the same OTC region as the runner VMs (eu-de, Germany). A miss is fetched from the upstream through fremforge’s egress and cached for your organisation’s next workflow.
Why this is automatic
A typical dependency install pulls tens of megabytes of packages every build. Without a cache, that traffic crosses the NAT gateway and pulls from the public index every time. With a cache, warm packages come from intra-region storage instead.
You do not need to configure anything: each tool honours its setting automatically. yarn and bun are not pointed at the proxy; they install from registry.npmjs.org through fremforge’s egress proxy.
Override per workflow
To override (e.g. when you need to pull from a private registry), set the variable in your workflow env:
jobs:
build:
runs-on: fremforge
env:
NPM_CONFIG_REGISTRY: https://npm.your-company.example/
steps:
- run: npm ciStep-level env: is also respected. The customer setting always wins over the fremforge default.
pnpm 11 does not read NPM_CONFIG_REGISTRY: set PNPM_CONFIG_REGISTRY to the same URL as well, or pnpm 11 keeps installing from registry.npmjs.org.
Container images
With runner pull-through on, the job’s Docker daemon pulls container images through your
organisation’s dependency proxy and they are cached for your organisation: docker pull,
docker run, a Dockerfile’s FROM, and the job and services: containers. Image names stay as
they are. This covers ghcr.io, quay.io, mcr.microsoft.com, registry.k8s.io,
public.ecr.aws and registry.access.redhat.com for every organisation. If the proxy cannot be
reached, or cannot serve a pull, the image is pulled from the registry directly. For images the
proxy is a cache, not a gate. A docker buildx builder with the docker-container driver pulls
directly. See Hosted runners for the details.
Docker Hub images
There is no shared Docker Hub mirror, and DOCKER_MIRROR_URL is no longer set. Docker Hub
(docker.io), like registry.redhat.io and registry.connect.redhat.com, goes through your
proxy only with your organisation’s own credential. Without one, a docker pull or FROM of a
Docker Hub image in a workflow is an anonymous pull straight from Docker Hub, subject to Docker
Hub’s rate limit for anonymous pulls.
Once an owner stores your organisation’s Docker Hub token in the dependency proxy, Docker Hub
pulls in your jobs go through the proxy and are cached, except in jobs from fork pull requests.
You can also pull through frem.sh/<org>/dependency-proxy/docker.io/ explicitly. See
Pull Docker Hub images through the dependency proxy with your own Docker Hub token.
npm audit and pnpm audit through the dependency proxy
npm audit and pnpm audit send their audit request to the registry they install from. When that registry is your organisation’s dependency proxy (https://frem.sh/_app/proxy/<org>/npm/), as it is for jobs with runner pull-through on, the proxy answers the request itself, in npm’s bulk-advisory format, from fremforge’s own advisory index built from OSV data. The request body is your exact dependency list, and the proxy does not forward it: it never reaches registry.npmjs.org or any other third-party advisory service. The request is authenticated like every other proxy request, with a frem.sh token that can read packages in the organisation.
- Supported clients: npm 7 and later, and pnpm 11 and later. Both send
POST /-/npm/v1/security/advisories/bulk, the one audit endpoint the proxy serves. The body may be gzipped, up to 4 MiB. - Not supported: npm 6 and pnpm 10 and earlier use the older audit endpoints (
/-/npm/v1/security/auditsand/-/npm/v1/security/audits/quick), which the proxy answers with404. Use npm 7 or later or pnpm 11, or pass--ignore-registry-errorsto pnpm. - pnpm 11 does not read
NPM_CONFIG_REGISTRY. It takes its registry fromPNPM_CONFIG_REGISTRY, an.npmrcorpnpm-workspace.yaml. Runner pull-through names the proxy in the job’s npmrc, which pnpm 11 reads. If you point a job at the proxy yourself withNPM_CONFIG_REGISTRY, setPNPM_CONFIG_REGISTRYto the same URL, or pnpm 11 installs from, and sends its audit to,registry.npmjs.org. - If the advisory index cannot be read, the proxy answers
503withRetry-After: 10instead of an empty report, so a failed lookup fails the audit rather than reading as “no vulnerabilities”. - In the report, advisory URLs point to
osv.dev, and each title ends with the OSV ID (a GHSA ID, for example). A known-malicious version is reported asMalicious package (MAL-…). The numeric advisoryidthat npm shows is derived from the OSV ID.
What is NOT cached
- Private packages. The proxy pulls npm, PyPI, Go, Maven, NuGet and the other package registries anonymously, so it serves public packages only, and your job’s own
Authorizationheader is never sent upstream. Privateghcr.ioandquay.ioimages, and Docker Hub and the Red Hat registries, use your organisation’s own upstream credential. For a private package registry, override the relevant variable (NPM_CONFIG_REGISTRY,PIP_INDEX_URL,GOPROXY, …). - Dynamic API calls. GitHub-style API calls (e.g.
npm view <package>againstapi.npmjs.org) are not cached, only the package-data registry endpoints are. - Source repos.
git clonetraffic to GitHub / GitLab / your own forge is not cached. Use the fremforge package registry (https://frem.sh/api/packages/<org>/<type>/) when you control the source. - Container images from other registries. Images from registries the dependency proxy does not know (gcr.io, a private registry) are pulled directly. Docker Hub and the Red Hat registries are cached only with your organisation’s own credential; see Docker Hub images. Configure a private registry directly per workflow.
Performance expectation
- Cold cache (first pull of a new package version): ~2-5s slower than upstream, the cache fetches from upstream, persists to OBS, then returns. Net latency = upstream pull + OBS write.
- Warm cache (subsequent pulls of the same version): ~30-60s faster than upstream for a typical
npm cirun, the cache serves from OBS intra-region. - Cache horizon: your retention setting, 90 days by default. A cached file not pulled for that long is removed; package versions and base images your organisation still uses are kept. See Retention.
Sovereignty story
The dependency proxy runs in fremforge’s eu-de cluster, and its store is an eu-de OBS bucket. It is fremforge’s own software, built and deployed like the rest of the platform. Upstream pulls are anonymous unless your organisation has stored its own credential for that upstream (for example Docker Hub); see Upstream credentials.
What the upstream registries receive is described under What the upstream sees. Each pull is recorded in your organisation’s registry access log (for CI cache hits, the pull census). The DPA covers both: the access log in Annex A.6, and the upstream registries, which are chosen on your instruction and are not sub-processors, in Annex B §B.9.
Status + incident response
The dependency proxy is monitored. Each job checks it when it starts: if it cannot be reached, that job installs and pulls from the public registries through fremforge’s egress instead, and the fallback is logged. Expected impact: ~30-60 s extra per uncached pull. A proxy that fails in the middle of a job is not routed around: npm and pnpm retry, and a busy proxy answers 503 with Retry-After (see Errors).
Related
- Runner image, what’s baked into the hosted runner image (kaniko, buildkit, language toolchains)
- Secrets, how to inject private-registry credentials per workflow
- Outbound egress, allowed destinations, and outbound-proxy behaviour are covered in CI runners