What is a pull-through cache?

A pull-through cache sits between a container client and an upstream registry. The first time an image is requested, the service pulls it from the upstream registry and caches it. Later requests can use the local cache, reducing repeated Internet transfers and improving pull speeds on campus. If an image is not yet cached, the first pull still depends on the availability and network performance of the upstream registry.

This service is available only from the ZJU campus network and does not require authentication. Do not publish the proxy address to off-campus users or use it as a public-facing service dependency.

This service maintains an allowlist, and only images on the allowlist are served. The allowlist is based on allows.txt from the DaoCloud public-image-mirror project: that file is the public list of the m.daocloud.io mirroring service, which has completed ICP filing in China, so the images it allows are guaranteed to be compliant and safe.

If an image you need is not on the allowlist, please open an issue at DaoCloud/public-image-mirror to request its addition.

Configure clients and keep original image names

Configure your client to keep the original image names, or change individual names as described under “Specify the proxy in an image name” below.

Client Configuration Coverage
Docker Engine / Docker Desktop registry-mirrors Docker Hub
Podman / Buildah / Skopeo Prefix mappings in registries.conf All registries proxied by this service
containerd hosts.toml Docker Hub with the configuration below
Standalone BuildKit / Buildx builder buildkitd.toml Docker Hub with the configuration below

Docker Engine / Docker Desktop

Merge this field into /etc/docker/daemon.json, preserving existing settings. JSON does not support comments.

{
  "registry-mirrors": ["https://docker.mirrors.zjusct.io"]
}

On Linux with systemd, apply the change with sudo systemctl restart docker. Restarting Docker may affect running containers, so choose a suitable time. Rootless Docker normally uses ~/.config/docker/daemon.json and systemctl --user restart docker. In Docker Desktop, merge the same configuration under Settings → Docker Engine and apply it.

Then use the original image names:

docker pull ubuntu:24.04
docker pull prom/prometheus:latest

Docker Hub image names in Compose files can also remain unchanged. registry-mirrors only applies to Docker Hub; images from ghcr.io, quay.io, and other registries still require the explicit proxy prefix below.

Docker may fall back to Docker Hub when the mirror is unavailable or denies a request, so a successful pull does not prove the cache was used. This configuration supports anonymous pulls of allowlisted images. Running docker login against the proxy does not automatically make those credentials available for pulls using original Docker Hub names. For service access tokens, use explicit names in the form registry.mirrors.zjusct.io/<project>/<image>.

Podman / Buildah / Skopeo: configure all registries

Save the complete configuration below as /etc/containers/registries.conf.d/99-zju-mirrors.conf. For personal configuration, use ~/.config/containers/registries.conf.d/99-zju-mirrors.conf. Existing user configuration may override system settings; check the effective rules and avoid duplicate definitions for the same prefix.

[[registry]]
prefix = "docker.io"
location = "registry.mirrors.zjusct.io/hub.docker.com"

[[registry]]
prefix = "ghcr.io"
location = "registry.mirrors.zjusct.io/ghcr.io"

[[registry]]
prefix = "quay.io"
location = "registry.mirrors.zjusct.io/quay.io"

[[registry]]
prefix = "nvcr.io"
location = "registry.mirrors.zjusct.io/nvcr.io"

[[registry]]
prefix = "registry.k8s.io"
location = "registry.mirrors.zjusct.io/registry.k8s.io"

[[registry]]
prefix = "registry.gitlab.com"
location = "registry.mirrors.zjusct.io/registry.gitlab.com"

[[registry]]
prefix = "public.ecr.aws"
location = "registry.mirrors.zjusct.io/public.ecr.aws"

[[registry]]
prefix = "mcr.microsoft.com"
location = "registry.mirrors.zjusct.io/mcr.microsoft.com"

[[registry]]
prefix = "docker.elastic.co"
location = "registry.mirrors.zjusct.io/docker.elastic.co"

[[registry]]
prefix = "gcr.io"
location = "registry.mirrors.zjusct.io/gcr.io"

[[registry]]
prefix = "k8s.gcr.io"
location = "registry.mirrors.zjusct.io/k8s.gcr.io"

[[registry]]
prefix = "us.gcr.io"
location = "registry.mirrors.zjusct.io/us.gcr.io"

[[registry]]
prefix = "lscr.io"
location = "registry.mirrors.zjusct.io/lscr.io"

[[registry]]
prefix = "docker.osgeo.org"
location = "registry.mirrors.zjusct.io/docker.osgeo.org"

Matching registry prefixes are replaced with proxy locations. No service restart is needed. These direct location mappings do not configure upstream fallback.

podman pull docker.io/library/ubuntu:24.04
podman pull quay.io/prometheus/prometheus:latest
skopeo inspect docker://registry.k8s.io/pause:3.10

Use fully qualified original names to avoid Podman short-name resolution prompts. Docker Hub official images use docker.io/library/<image>. These settings do not configure Docker daemon or Kubernetes’ containerd runtime.

containerd, including Kubernetes nodes using containerd

Create /etc/containerd/certs.d/docker.io/hosts.toml:

server = "https://registry-1.docker.io"

[host."https://docker.mirrors.zjusct.io"]
  capabilities = ["pull", "resolve"]

Enable this directory in the CRI plugin. Merge the appropriate section into /etc/containerd/config.toml; do not replace the whole file with these fragments.

For containerd 2.x with configuration version 3:

[plugins."io.containerd.cri.v1.images".registry]
  config_path = "/etc/containerd/certs.d"

For containerd 1.x with configuration version 2:

[plugins."io.containerd.grpc.v1.cri".registry]
  config_path = "/etc/containerd/certs.d"

Restart containerd after initially changing config_path. Once the directory is enabled, updates to hosts.toml normally require no restart. Distributions such as K3s and RKE2 may generate runtime configuration themselves; use their supported registry configuration mechanism.

For CRI:

sudo crictl pull docker.io/library/ubuntu:24.04

ctr does not read the CRI plugin configuration; pass the directory explicitly:

sudo ctr images pull --hosts-dir /etc/containerd/certs.d docker.io/library/ubuntu:24.04

The server above preserves direct fallback to Docker Hub. Use explicit proxy image names for other registries. Merely adding a Harbor project path to a hosts.toml URL does not correctly translate authentication scopes.

Standalone BuildKit / Docker Buildx

Do not assume a separate BuildKit daemon inherits Docker daemon mirror settings. Merge this into buildkitd.toml:

[registry."docker.io"]
  mirrors = ["docker.mirrors.zjusct.io"]

A standalone buildkitd must load that file and restart. For Buildx’s docker-container driver, create a builder using it:

docker buildx create --name zju-mirror --driver docker-container --buildkitd-config ./buildkitd.toml --use
docker buildx inspect --bootstrap

Dockerfiles can retain FROM ubuntu:24.04. This configuration only covers Docker Hub, and the client may still try the upstream if the mirror fails.

Specify the proxy in an image name

Add registry.mirrors.zjusct.io/ before the original image name and retain the original registry hostname. For example:

docker pull registry.mirrors.zjusct.io/ghcr.io/owner/image:tag
docker pull registry.mirrors.zjusct.io/registry.k8s.io/pause:3.10
docker pull registry.mirrors.zjusct.io/quay.io/prometheus/prometheus:latest

Docker Hub images

Docker Hub image names often omit the registry hostname, and official images also omit the library/ namespace. Both parts must be explicit when pulling through this proxy:

Common notation Pull through the proxy
ubuntu:24.04 registry.mirrors.zjusct.io/hub.docker.com/library/ubuntu:24.04
library/nginx:stable registry.mirrors.zjusct.io/hub.docker.com/library/nginx:stable
prom/prometheus:latest registry.mirrors.zjusct.io/hub.docker.com/prom/prometheus:latest

In other words:

  • if an image name has no registry hostname, add hub.docker.com/;
  • if it has only one path component, it is a Docker Official Image, so also add library/;
  • finally, add registry.mirrors.zjusct.io/ before the complete name.

The same complete image name works in Pod manifests, Compose files, and other tools compatible with OCI/Docker registries. For example:

image: registry.mirrors.zjusct.io/hub.docker.com/library/redis:7

Proxied registries

Proxy caches are currently available for these upstream registries:

Registry Proxy path prefix
Docker Hub registry.mirrors.zjusct.io/hub.docker.com/
GitHub Container Registry registry.mirrors.zjusct.io/ghcr.io/
Quay registry.mirrors.zjusct.io/quay.io/
NVIDIA Container Registry registry.mirrors.zjusct.io/nvcr.io/
Kubernetes Registry registry.mirrors.zjusct.io/registry.k8s.io/
GitLab Container Registry registry.mirrors.zjusct.io/registry.gitlab.com/
Amazon ECR Public Gallery registry.mirrors.zjusct.io/public.ecr.aws/
Microsoft Artifact Registry registry.mirrors.zjusct.io/mcr.microsoft.com/
Elastic Container Registry registry.mirrors.zjusct.io/docker.elastic.co/
Google Container Registry registry.mirrors.zjusct.io/gcr.io/
Legacy Kubernetes GCR registry.mirrors.zjusct.io/k8s.gcr.io/
Google US Container Registry registry.mirrors.zjusct.io/us.gcr.io/
LinuxServer.io registry.mirrors.zjusct.io/lscr.io/
OSGeo Docker Registry registry.mirrors.zjusct.io/docker.osgeo.org/