Pulling through a cache instead of authenticating

A registry rate limit hit every weekday at nine, and three options of which the cheapest was not the one we tried first.

# a pull-through cache, which is one container
services:
  registry-cache:
    image: registry:2
    environment:
      REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io
    volumes: ['cache:/var/lib/registry']

# and on the runners
# /etc/docker/daemon.json
{ "registry-mirrors": ["http://registry-cache.internal:5000"] }

A mirror is transparent — no image references change, no pipeline edits — which is what makes it the right first answer rather than the third. It also becomes a dependency on the critical path for every build, so it needs a health check and a documented fallback to the upstream registry, and the fallback is one line in the daemon configuration that somebody has to know exists.