Private Registries & Pull-Through Mirrors
Use registries.yaml to authenticate, mirror Docker Hub through a cache, and dodge public rate limits.
Every node that pulls images reads /etc/rancher/k3s/registries.yaml. Two features make it worth mastering: authenticated pulls and pull-through mirrors.
Rewrite mirrors
A mirrors entry redirects all pulls for a registry to a different endpoint — typically a caching proxy. This is how you stop hammering Docker Hub and escape its anonymous pull-rate limits: mirror docker.io through a local registry running in pull-through mode. Existing manifests need no changes; the rewrite is transparent.
Auth and TLS
The configs block holds credentials and TLS settings per registry host, including a custom CA for self-signed internal registries or insecure_skip_verify for lab setups.
Rollout discipline
- The file must exist on every node that pulls images — servers and agents alike.
- k3s reads it at startup, so
systemctl restart k3s(ork3s-agent) after edits. - Verify the wiring with
crictl pull, not just a Pod — you get the raw error immediately.
Example
# /etc/rancher/k3s/registries.yaml (on EVERY node)
mirrors:
docker.io:
endpoint:
- "https://registry-cache.internal:5000"
registry.internal:5000:
endpoint:
- "https://registry.internal:5000"
configs:
registry.internal:5000:
auth:
username: deploy
password: s3cret
tls:
ca_file: /etc/ssl/certs/internal-ca.crt
# sudo systemctl restart k3s (or k3s-agent on workers)
# sudo k3s crictl pull registry.internal:5000/app:1.0When to use it
- A team configures a pull-through cache for Docker Hub in registries.yaml to avoid hitting the Docker Hub anonymous pull rate limit in CI.
- An air-gapped factory cluster points all image pulls to an internal Harbor instance so workers never need internet access.
- An operator mirrors multiple public registries (docker.io, gcr.io, quay.io) through a single local proxy to reduce bandwidth and latency for edge sites.
More examples
Mirror Docker Hub through a local cache
Routes all docker.io and gcr.io image pulls through internal caching proxies to bypass rate limits and reduce latency.
# /etc/rancher/k3s/registries.yaml
mirrors:
docker.io:
endpoint:
- 'https://hub-cache.internal.example.com'
gcr.io:
endpoint:
- 'https://gcr-cache.internal.example.com'Add auth for a private registry
Authenticates to a private Harbor registry with a robot account and a custom CA certificate for TLS verification.
# /etc/rancher/k3s/registries.yaml
configs:
'registry.example.com':
auth:
username: robot$ci
password: harbor-robot-token
tls:
ca_file: /etc/ssl/certs/harbor-ca.crtReload and test registry config
Restarts k3s to pick up registries.yaml changes, checks the logs for registry loading, and verifies a private image pull.
systemctl restart k3s
journalctl -u k3s -n 10 | grep -i 'registry\|mirror'
kubectl run ping --image=registry.example.com/myapp:v1 \
--restart=Never --rm -it -- echo ok
Discussion