Skip to main content

Registry cache

Without a local image cache, a cluster cannot restart when upstream registries are unreachable. Running pods survive — their layers are already on the node — but any restart, reschedule or scale-up fails to pull, and the service stays down.

That is the worst shape of failure: invisible until you need recovery, and it bites hardest when something else is already broken.

Two everyday benefits besides outages:

  • Rate limits. Anonymous pulls from public registries are throttled per IP; a cluster redeploying a stack can hit the limit mid-deploy.
  • Speed and bandwidth. Re-deploying a full stack re-pulls gigabytes otherwise — and developer machines can use the same cache.

It must live outside the cluster

A registry inside the cluster cannot serve the images required to start that cluster. After a cold boot the cache's own pod needs an image, which needs the cache. (In-cluster peer-to-peer layer sharing is useful for multi-node clusters but does not solve cold start.)

Shape

registry:2 proxies exactly one upstream, so one instance per registry:

PortUpstream
5000Docker Hub
5001registry.k8s.io
5002ghcr.io
5003quay.io

Alternatives worth evaluating: zot (multiple upstreams in one instance) or Harbor (heavier, but garbage collection and auth included).

Point the cluster at them — for k3s, /etc/rancher/k3s/registries.yaml:

mirrors:
docker.io:
endpoint: ["http://<cache-host>:5000"]
registry.k8s.io:
endpoint: ["http://<cache-host>:5001"]

k3s renders certs.d/<registry>/hosts.toml from this on restart. Upstream remains the fallback, so the cache fails open.

⚠️ Warming the cache — the trap

crictl pull does not warm the cache when the image is already on the node. It is a no-op that never contacts the mirror. On the reference build a warming loop reported pulled ok=41 failed=0 while the cache actually held four repositories.

Request each image from the mirror endpoint instead:

skopeo copy --src-tls-verify=false \
docker://<cache-host>:5000/library/<image>:<tag> dir:/tmp/warm && rm -rf /tmp/warm

⚠️ Official Docker Hub images need library/ inserted in the mirror path.

Verify with /v2/_catalog, never with the pull command's exit status.

Verify it actually works

The only meaningful test: block internet egress on both the cache host and a cluster node, remove an image from the node, and pull it again. It should succeed from cache. Remember to remove the test firewall rules afterwards.

Housekeeping

The cache grows as tags churn. registry:2 needs REGISTRY_STORAGE_DELETE_ENABLED=true plus a periodic registry garbage-collect. The cache is fully reconstructible, so it is a reasonable candidate to exclude from offsite backup — and it must be re-warmed after deploying new services.