Skip to main content

Package caches

Every fremforge-hosted runner VM boots with these environment variables pre-set so your CI hits an in-region cache for the public registry traffic that would otherwise leave the EU:

VariableValueWhat it caches
NPM_CONFIG_REGISTRY (+ COREPACK_NPM_REGISTRY, YARN_NPM_REGISTRY_SERVER, BUN_CONFIG_REGISTRY)http://npm-cache.frem.sh/Public packages from registry.npmjs.org (npm / pnpm / yarn / bun)
PIP_INDEX_URL (+ PIP_TRUSTED_HOST, UV_DEFAULT_INDEX)in-region PyPI cache (proxpi)Public packages from PyPI (pip / uv)
GOPROXYin-region Go cache (Athens)Public Go modules from proxy.golang.org; GOSUMDB checksum validation is preserved through the cache
DOCKER_MIRROR_URLhttp://docker-mirror.frem.sh/Public images from registry-1.docker.io (Docker Hub)
TRIVY_DB_REPOSITORY, TRIVY_JAVA_DB_REPOSITORYin-region ghcr.io/aquasecurity mirrorTrivy’s vulnerability database — a trivy scan pulls the DB from the EU cache instead of ghcr.io directly

The caches sit in the same OTC region as the runner VMs (eu-de, Germany). Misses fall through to upstream via fremforge’s egress 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 ci

Step-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. Every cache is configured for anonymous-only upstream pulls. Customer Authorization headers never reach upstream and are never cached. If you need a private package, override the relevant variable (NPM_CONFIG_REGISTRY, PIP_INDEX_URL, GOPROXY, …) or your Docker registry config per the examples above.
  • Dynamic API calls. GitHub-style API calls (e.g. npm view <package> against api.npmjs.org) are not cached, only the package-data registry endpoints are.
  • Source repos. git clone traffic to GitHub / GitLab / your own forge is not cached. Use Forgejo’s own packages registry (packages.frem.sh) when you control the source.
  • Other image registries + private registries. General image pulls from quay.io, gcr.io, or ghcr.io are not mirrored — only Docker Hub is (the one exception is Trivy’s vuln-DB, pulled from the in-region ghcr.io/aquasecurity mirror as noted above). Private registries are never proxied; configure them 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 ci run, 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 caches run in fremforge’s eu-de cluster; the cache storage lives in an eu-de OBS bucket. No customer personal data ever transits these systems, they only see public package binaries.

The cache software (Verdaccio for npm, proxpi for PyPI, Athens for Go modules, and distribution for Docker Hub) is open source and runs from fremforge’s own SWR mirror — the runtime binaries follow 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 any of them.

Status + incident response

Cache health is monitored. When a cache is degraded, workflows continue to run — the runner VMs fall through to the upstream registry via fremforge’s egress, with no job failures. Expected impact: ~30-60 s extra per uncached pull.

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