Cloud Native 22 min read

How to Build a Fast Docker Registry Proxy in China with Kubernetes and Envoy

This guide shows how to set up a self‑hosted Docker registry proxy using Docker's registry image, Kubernetes (k3s), and Envoy to accelerate access to public image repositories like gcr.io, quay.io, and Docker Hub from within China, covering prerequisites, custom Dockerfile, deployment manifests, Envoy configuration, authentication, and cache cleanup.

Programmer DD
Programmer DD
Programmer DD
How to Build a Fast Docker Registry Proxy in China with Kubernetes and Envoy

When using Docker and Kubernetes in China, access to public registries such as gcr.io and quay.io is blocked, leaving only the slow Docker Hub. This article explains how to build a local registry proxy that caches these images.

1. Prerequisites

A server that can reach the upstream registries (e.g., gcr.io)

A domain name and SSL certificate (Let’s Encrypt is sufficient)

2. Core Idea

The Docker registry project can act as a pull‑through cache. By setting the remoteurl parameter, the private registry forwards requests to the upstream registry and caches the layers locally.

3. Custom Registry

Create a Dockerfile that extends the official registry image and adds an entrypoint script to inject environment variables into the configuration.

FROM registry:2.6
LABEL maintainer="registry-proxy Docker Maintainers https://fuckcloudnative.io"
ENV PROXY_REMOTE_URL=""
ENV DELETE_ENABLED=""
COPY entrypoint.sh /entrypoint.sh

The entrypoint.sh script writes the proxy settings, CORS headers, and optional delete configuration into /etc/docker/registry/config.yml before launching the registry.

#!/bin/sh
set -e
CONFIG_YML=/etc/docker/registry/config.yml
if [ -n "$PROXY_REMOTE_URL" -a `grep -c "$PROXY_REMOTE_URL" $CONFIG_YML` -eq 0 ]; then
  echo "proxy:" >> $CONFIG_YML
  echo "  remoteurl: $PROXY_REMOTE_URL" >> $CONFIG_YML
  echo "  username: $PROXY_USERNAME" >> $CONFIG_YML
  echo "  password: $PROXY_PASSWORD" >> $CONFIG_YML
  echo "------ Enabled proxy to remote: $PROXY_REMOTE_URL ------"
elif [ $DELETE_ENABLED = true -a `grep -c "delete:" $CONFIG_YML` -eq 0 ]; then
  sed -i '/rootdirectory/a\  delete:' $CONFIG_YML
  sed -i '/delete/a\    enabled: true' $CONFIG_YML
  echo "------ Enabled local storage delete -----"
fi
sed -i "/headers/a\    Access-Control-Allow-Origin: ['*']" $CONFIG_YML
sed -i "/headers/a\    Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']" $CONFIG_YML
sed -i "/headers/a\    Access-Control-Expose-Headers: ['Docker-Content-Digest']" $CONFIG_YML
case "$1" in
  *.yaml|*.yml) set -- registry serve "$@" ;;
  serve|garbage-collect|help|-*) set -- registry "$@" ;;
esac
exec "$@"

4. Deploy the Cache Service

Build the image and run it, or use the pre‑built image yangchuansheng/registry-proxy. Deploy it in a k3s cluster with a Deployment and Service manifest (example shown for docker.io).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dockerhub
  labels:
    app: dockerhub
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dockerhub
  template:
    metadata:
      labels:
        app: dockerhub
    spec:
      dnsPolicy: None
      dnsConfig:
        nameservers:
        - 8.8.8.8
        - 8.8.4.4
      containers:
      - name: dockerhub
        image: yangchuansheng/registry-proxy:latest
        env:
        - name: PROXY_REMOTE_URL
          value: "https://registry-1.docker.io"
        - name: PROXY_USERNAME
          value: "yangchuansheng"
        - name: PROXY_PASSWORD
          value: "********"
        ports:
        - containerPort: 5000
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/localtime
          name: localtime
        - mountPath: /var/lib/registry
          name: registry
      volumes:
      - name: localtime
        hostPath:
          path: /etc/localtime
      - name: registry
        hostPath:
          path: /var/lib/registry
---
apiVersion: v1
kind: Service
metadata:
  name: dockerhub
  labels:
    app: dockerhub
spec:
  selector:
    app: dockerhub
  ports:
  - protocol: TCP
    name: http
    port: 5000
    targetPort: 5000

5. Proxy Selection

If you only cache docker.io, you can expose the proxy on port 443 and add SSL. For multiple registries, use an edge proxy (Ingress) that routes by domain name. Envoy is recommended for its dynamic configuration API.

6. Envoy Configuration

Deploy Envoy as a Deployment with two replicas, exposing ports 80 and 443. Mount the configuration files via hostPath and edit envoy.yaml, lds.yaml, and cds.yaml to define listeners, routes, and clusters for each upstream registry.

# envoy.yaml (snippet)
node:
  id: node0
  cluster: cluster0
dynamic_resources:
  lds_config:
    path: /etc/envoy/lds.yaml
  cds_config:
    path: /etc/envoy/cds.yaml
admin:
  access_log_path: "/dev/stdout"
  address:
    socket_address:
      address: "0.0.0.0"
      port_value: 15001

7. Verify Acceleration

Pull an image through the proxy, e.g.:

docker pull docker.fuckcloudnative.io/library/nginx:alpine

The pull completes quickly, confirming the cache works.

8. Cache All Registries

Repeat the Deployment and Envoy configuration for other registries ( gcr.io, k8s.gcr.io, quay.io, ghcr.io) by adjusting PROXY_REMOTE_URL and adding corresponding virtual hosts and clusters in Envoy.

9. Runtime Configuration

Configure Docker, containerd, or Podman to use the proxy as a registry mirror. Example for Docker ( /etc/docker/daemon.json):

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

For containerd ( /etc/containerd/config.toml) and Podman ( /etc/containers/registries.conf) similar mirror entries are added.

10. Clean Up Cache

Deploy a separate registry with the delete feature enabled to remove old layers, or schedule a cron job that periodically deletes /var/lib/registry/*. A UI such as joxit/docker-registry-ui can be added for manual cleanup.

11. Authentication

Protect the proxy with basic auth using an htpasswd file stored in a Kubernetes Secret. Add the following environment variables to the Deployment:

- name: REGISTRY_AUTH_HTPASSWD_REALM
  value: "Registry Realm"
- name: REGISTRY_AUTH_HTPASSWD_PATH
  value: "/auth/htpasswd"

Mount the secret as a volume at /auth. Users must log in with docker login docker.fuckcloudnative.io before pulling images.

12. Optional Token Authentication

For finer‑grained access control, integrate a token authentication server such as docker_auth.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Cloud NativeDockerKubernetesEnvoyContainer RegistryRegistry Proxy
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.