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.