Automate Docker Image Mirroring to CNB with GitHub Actions and skopeo
This guide explains how to set up a fully automated pipeline that syncs Docker Hub images to the CNB registry using GitHub Actions, skopeo, and configurable YAML files, supporting selective or full mirroring, version overrides, and secure credential handling.
Mirroring frequently used open‑source Docker images to a China‑accessible registry improves download speed, stability, and avoids unreliable official sources.
Background
Docker Hub often suffers from network latency, rate limits, or connection failures, making image pulls unreliable. The previous workflow synced images to CODING, which has been discontinued, so the target registry was switched to CNB (cnb.cool), a Tencent‑backed cloud‑native registry.
Sync Overview
The list of images to mirror is stored in .github/images.yml.
The sync can be triggered manually or automatically via GitHub Actions.
The skopeo tool copies images from Docker Hub to CNB.
Both selective (single image) and full (all images) syncs are supported.
Specific tags can be overridden at runtime.
GitHub Actions Workflow
The workflow file docker-proxy.yml defines two inputs ( name and version), runs on ubuntu-latest, and performs the following steps:
name: Mirror Docker Images to CNB
on:
workflow_dispatch:
inputs:
name:
description: 'Select image to mirror (or leave blank to mirror all)'
required: false
type: choice
options:
- ""
- vaultwarden
- bark-server
- elasticsearch
- mysql
- hyperf
- clickhouse
version:
description: 'Override tag version'
required: false
type: string
push:
paths:
- '.github/images.yml'
branches: ['main']
jobs:
mirror:
name: >-
Mirror ${{ github.event.inputs.name || 'All Images' }}${{ github.event.inputs.version && format(' (version: {0})', github.event.inputs.version) || '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Mirror images
env:
INPUT_NAME: ${{ github.event.inputs.name }}
INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
images=$(yq -o json '.images' ${{ github.workspace }}/.github/images.yml)
if [ -n "$INPUT_NAME" ]; then
matrix=$(echo "$images" | jq -c --arg name "$INPUT_NAME" '.[] | select(.name == $name)')
else
matrix=$(echo "$images" | jq -c '.[]')
fi
if [ -z "$matrix" ]; then
echo "No matching images found for name: $INPUT_NAME"
exit 1
fi
echo "$matrix" | while read -r item; do
image=$(echo "$item" | jq -r '.image')
name=$(echo "$item" | jq -r '.name')
default_tag=$(echo "$item" | jq -r '.tag')
tag=${INPUT_VERSION:-$default_tag}
echo "Mirroring $image:$tag to docker.cnb.cool/lufei/docker/$name:$tag"
skopeo copy --all docker://docker.io/${image}:${tag} \
docker://docker.cnb.cool/lufei/docker/${name}:${tag} \
--src-creds "${{ secrets.DOCKERHUB_USERNAME }}:${{ secrets.DOCKERHUB_TOKEN }}" \
--dest-creds "cnb:${{ secrets.CNB_DOCKER_TOKEN }}"
echo "::notice title=Image Published::https://docker.cnb.cool/lufei/docker/${name}:${tag}"
doneTrigger Mechanisms
Manual trigger : Run the workflow from the GitHub Actions UI, optionally specifying an image name and tag.
Automatic trigger : Any change to .github/images.yml on the main branch automatically starts a full sync.
Image List File
The .github/images.yml file defines each source image, its default tag, and a short name used for the target repository:
images:
- image: "vaultwarden/server"
tag: "latest"
name: "vaultwarden"
- image: "finab/bark-server"
tag: "latest"
name: "bark-server"
# ... more entries ...Core Sync Command
skopeo copy --all docker://docker.io/${image}:${tag} \
docker://docker.cnb.cool/lufei/docker/${name}:${tag} \
--src-creds "${{ secrets.DOCKERHUB_USERNAME }}:${{ secrets.DOCKERHUB_TOKEN }}" \
--dest-creds "cnb:${{ secrets.CNB_DOCKER_TOKEN }}" --allensures multi‑architecture images (e.g., amd64, arm64) are mirrored.
Credentials are supplied via GitHub Secrets for security.
After each successful copy, the workflow prints a direct pull URL.
How to Use
Manual Trigger
Navigate to GitHub → Actions → Mirror Docker Images to CNB → Run workflow . Leave the image name empty to sync all images, or specify a name and optional tag to sync a single image.
Automatic Trigger
Whenever .github/images.yml is updated and pushed to the main branch, the workflow runs automatically and mirrors all listed images.
Pulling Mirrored Images
After a successful sync, pull images directly from the CNB public endpoint:
docker pull docker.cnb.cool/lufei/docker/hyperf:8.3-alpine-v3.21-swooleAppending https:// to the URL opens a web page with image details.
Secure Configuration (GitHub Secrets)
DOCKERHUB_USERNAME: Username for pulling source images from Docker Hub. DOCKERHUB_TOKEN: Docker Hub authentication token. CNB_DOCKER_TOKEN: Credential for pushing to the CNB registry.
CNB stores Docker artifacts directly in the repository, so creating a repository is sufficient for Docker images.
Conclusion
Combining GitHub Actions, skopeo, and the CNB service provides a reusable, automated pipeline that reliably mirrors multiple Docker images, significantly improving image availability and deployment efficiency.
References
CNB registry: https://cnb.cool/ .github/images.yml file: https://github.com/sy-records/.github/blob/main/.github/images.yml
Workflow definition docker-proxy.yml: https://github.com/sy-records/.github/blob/main/.github/workflows/docker-proxy.yml
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
