Package caches
Every fremforge-hosted runner pod boots with two environment variables pre-set so your CI hits an in-region cache for the public registry traffic that would otherwise leave the EU:
| Variable | Value | What it caches |
|---|---|---|
NPM_CONFIG_REGISTRY | http://cache-verdaccio.fremforge-prd.svc.cluster.local:4873/ | Public packages from registry.npmjs.org |
DOCKER_MIRROR_URL | http://cache-distribution.fremforge-prd.svc.cluster.local:5000 | Public images from registry-1.docker.io |
The caches sit in the same OTC region as the runner pods (eu-de, Germany). Misses fall through to upstream via fremforge’s outbound proxy and the response is cached for the next workflow.
Why this is automatic
A typical npm ci pulls 30-60 MB of tarballs every build. Without the cache, that traffic crosses the NAT gateway and pulls from us-east-1 every time. With the cache, the same install drops to ~3-5s of intra-region traffic on warm packages.
You do not need to configure anything, npm, pnpm, and yarn v1 all honour NPM_CONFIG_REGISTRY automatically.
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.
Docker Hub mirror
The Docker mirror is anonymous-only and only works against public Docker Hub images. Two consumption patterns:
kaniko (the default for in-runner image builds)
- name: Build image
uses: int128/kaniko-action@v1
with:
context: .
push: true
extra-args: --registry-mirror=${{ env.DOCKER_MIRROR_URL }}buildkit (docker buildx)
Configure the daemon at job start, then build as normal:
- name: Configure docker buildx mirror
run: |
mkdir -p /home/runner/.docker
cat > /home/runner/.docker/config.json <<EOF
{ "registry-mirrors": ["${DOCKER_MIRROR_URL}"] }
EOF
- uses: docker/setup-buildx-action@v3
- run: docker buildx build .What is NOT cached
- Private packages. Both caches are configured for anonymous-only upstream pulls. Customer
Authorizationheaders never reach upstream and are never cached. If you need a private package, overrideNPM_CONFIG_REGISTRYor your Docker registry config per the examples above. - 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 Forgejo’s own packages registry (packages.frem.sh) when you control the source. - Anything outside the public registries. The caches do not proxy quay.io, gcr.io, ghcr.io, or any private registry. Configure those 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: 90 days. Versions not requested for 90 days are evicted by the lifecycle policy. Practical effect: anything in active use stays warm forever.
Sovereignty story
The cache pods run inside fremforge-prd CCE; the cache storage lives in the eu-de OBS bucket fremforge-prd-cache-mirrors. No customer personal data ever transits these systems, they only see public package binaries.
The cache software itself (Verdaccio + distribution/distribution) is governed by the OpenJS and CNCF foundations respectively, both US-incorporated. The runtime binaries are mirrored to fremforge’s own SWR registry (eu-de) following the same supply-chain pattern as every other container image fremforge runs. Anonymous-only upstream pulls mean no customer credentials ever leave fremforge’s network.
DPA Annex B (sub-processor disclosure) does not list these caches because they handle internal tooling traffic only, no customer personal data is processed by either cache.
Status + incident response
Cache health is monitored by the cluster-internal cache-mirrors-availability watchdog (every 15 min) and an OBS cost-guard CES alarm fires at 100 GiB bucket size. Operator runbooks:
When a cache is degraded, workflows continue to run, the runner pods are configured to fall through to the upstream registry via fremforge’s outbound proxy. Expected impact: ~30-60s extra per uncached pull. No job failures.
Related
- Runner image, what’s baked into the runner-base 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