Registry mirrors
Every klimax VM runs a set of pull-through registry caches, wired into each cluster automatically. They exist for one reason: so you stop hitting Docker Hub's rate limits and stop re-downloading the same images every time you rebuild a cluster.
Why registry mirrors
A klimax workflow means creating and destroying clusters constantly.
Without mirrors, every rebuild re-pulls the same base images
(nginx, metallb, your app's dependencies)
straight from the public registries. Two problems follow:
- Docker Hub rate limits. Anonymous pulls from
docker.ioare throttled per source IP. On a shared network — an office, a VPN, CI — you can burn through the quota fast and start seeingtoomanyrequests: You have reached your pull rate limit. - Cold-cache rebuilds are slow. Re-pulling hundreds
of megabytes on every
cluster createwastes minutes and bandwidth you don't need to spend.
klimax fixes both by running a pull-through cache in
front of each upstream registry. The first pull of an image fetches it
from the internet and stores the blobs locally; every pull after that —
across every cluster, and across cluster rebuilds — is served
from that local cache. With the default host-backed cache, the blobs
even survive a full klimax destroy.
Authenticate to lift the Docker Hub limit further
Caching already collapses repeated pulls into one, but the very first
pull of each image still counts against the anonymous quota. Add your
Docker Hub credentials to the registry-dockerio mirror to
pull as an authenticated user, which raises the limit substantially:
registries:
mirrors:
- name: "registry-dockerio"
port: 5030
remoteURL: "https://registry-1.docker.io"
username: "your-dockerhub-username" # optional
password: "your-dockerhub-password" # optional (use an access token)
Prefer a Docker Hub access token over your account
password — it's scoped and revocable. The credential lives in
~/.klimax/config.yaml and is used only by the mirror
container inside the VM.
How clusters use them
The mirrors run as plain registry:2 containers inside the
VM, each joined to the kind Docker network. When klimax
creates a cluster it configures that cluster's containerd —
via certs.d hosts.toml files (the containerd 2.x
way) — so pulls from docker.io, quay.io,
gcr.io, and Google Artifact Registry
(us-docker.pkg.dev, us-central1-docker.pkg.dev)
are transparently redirected to the matching mirror.
Because every kind node and every mirror share the kind
network, nodes resolve mirrors by hostname (e.g.
registry-dockerio:5030) via Docker's embedded DNS — no
per-pod config, no image-name rewriting in your manifests.
Adding a registry mirror
Any registry with a standard v2 API can be mirrored. Append an entry to
registries.mirrors with a unique name, a free
host port, and the upstream remoteURL — for
example GitHub Container Registry:
registries:
mirrors:
# ...the defaults...
- name: "registry-ghcrio"
port: 5060
remoteURL: "https://ghcr.io"
username: "your-github-username" # optional, if the upstream needs auth
password: "ghp_..."
Apply it by recreating the mirror containers, then rebuild any clusters
that should route through the new mirror (the containerd
patch is applied at cluster-create time):
$ klimax down && klimax up # restart the registry containers
$ klimax cluster create dev # new clusters pick up the mirror
Don't name a mirror after its hostname. A mirror
container joins the shared kind Docker network, so a name like
quay.io would make Docker's embedded DNS resolve
quay.io to the container itself — the proxy then resolves its
own upstream to itself and pulls fail. klimax rejects any mirror
name containing . or :; use a safe
form like registry-quayio. (Renaming a mirror also means
removing its old container — docker rm -f it, then
klimax up.)
Set mirrors to an empty list ([]) to disable
all mirrors and pull straight from the upstreams.
The defaults
Out of the box, klimax runs five pull-through mirrors. You don't
configure any of this to get started — it's the default
registries block:
| Container | Port | Role |
|---|---|---|
registry-dockerio | 5030 | pull-through cache for https://registry-1.docker.io |
registry-quayio | 5010 | pull-through cache for https://quay.io |
registry-gcrio | 5020 | pull-through cache for https://gcr.io |
registry-us-docker-pkgdev | 5040 | pull-through cache for https://us-docker.pkg.dev |
registry-us-central1-docker-pkgdev | 5050 | pull-through cache for https://us-central1-docker.pkg.dev |
The cache location is set by registries.cacheStorage, which
defaults to "host". See
Configuration for the complete
block.
Getting your own images into a cluster
klimax has no local push registry — load a locally built image straight
into a cluster's nodes with kind load. Point your host
Docker at the VM, build there, then load the image (the kind CLI lives in
the VM, so run it from klimax shell):
$ eval "$(klimax docker-env)" # DOCKER_HOST → the klimax VM
$ docker build -t myapp:test . # image now lives in the VM's Docker
$ klimax shell # open an SSH session in the VM
$ kind load docker-image myapp:test --name dev
Then reference it in a manifest as myapp:test and pin the
pull policy so the node uses the loaded image instead of pulling from a
registry:
image: myapp:test
imagePullPolicy: IfNotPresent
Public images pull transparently through the built-in mirrors above —
no extra steps. kind load is only for images you build
locally and haven't pushed anywhere.
Cache persistence
Where the mirror cache lives is controlled by
registries.cacheStorage:
"host"(default) — cache at~/.klimax/registry-cache/<name>/on the Mac, virtiofs-mounted into the VM. Survivesklimax down/upandklimax destroy."guest"— cache at/var/lib/klimax/registry-cache/<name>/inside the VM only; wiped onklimax destroy.
With host storage, blobs persist across full teardowns — rebuilding a lab pulls from the local cache instead of the internet, and that first cold pull is a one-time cost.
Resetting the cache
To wipe every mirror's cache and start fresh:
$ klimax registry clean-cache
This removes all mirror containers and deletes their cache directories
(on the host or in the guest, per cacheStorage); re-run
klimax up to recreate them cold. To wipe everything
— VM, clusters, and cache — see
wiping and reinstalling.
See also
- Configuration — the full
registriesblock and every other tunable. - Networking & L3 routing — the
kindDocker network the registries join. - Getting started — wipe & reinstall — start completely from scratch.