Your lightweight Docker registry sync engine.
Sync images from Docker Hub to ECR, ACR, GCR & GHCR — beat rate limits, run anywhere.
Docker Hub's rate limits can block pulls in CI and production. SyncerD copies images from Docker Hub to your own registries (AWS ECR, Azure ACR, Google GCR, GitHub Container Registry) so you pull from your registry instead — no rate-limit headaches, same images.
- One config, many registries — Sync the same set of images to ECR, ACR, GCR, and GHCR from a single YAML.
- Runs everywhere — CLI, GitHub Actions, Kubernetes (Helm CronJob). Stateless by default; no DB required.
- New tags, automatically — Watches source tags and syncs only what's missing; optional Slack alerts on new syncs or failures.
1. Install (binary or Go)
# Binary (see Releases for your OS)
go install github.com/clouddrove/syncerd@latest2. Add a config — e.g. syncerd.yaml:
source:
type: dockerhub
registry: docker.io
destinations:
- name: my-ecr
type: ecr
registry: 123456789012.dkr.ecr.us-east-1.amazonaws.com
region: us-east-1
images:
- name: library/nginx
watch_tags: true3. Run (use Docker credentials for destinations; see Auth)
export SYNCERD_SOURCE_USERNAME=your-dockerhub-user
export SYNCERD_SOURCE_PASSWORD=your-dockerhub-token
./syncerd sync --onceThat's it. Use the same config in GitHub Actions or Kubernetes (Helm) for scheduled syncs.
| Feature | Description |
|---|---|
| Multi-registry | Sync to AWS ECR, Azure ACR, Google GCR, GitHub Container Registry |
| Auto tag watch | Detects new tags and syncs only what's missing (optional persistent state) |
| Scheduled runs | Built-in cron (e.g. every 3 weeks) or use K8s CronJob / GitHub Actions |
| GitHub Action | Marketplace action — drop into workflows |
| Helm chart | Run as a CronJob on Kubernetes; stateless by default (no PVC) |
| Slack | Optional notifications on new syncs and failures (compact/detailed) |
| Secure | Docker Hub via env/secret; destinations via Docker credential config |
| Method | Command |
|---|---|
| Go install | go install github.com/clouddrove/syncerd@latest |
| From source | git clone https://github.com/clouddrove/syncerd.git && cd syncerd && go build -o syncerd ./main.go |
| Releases | Download the latest release for your OS/arch |
| Docker | docker run ghcr.io/clouddrove/syncerd:latest syncerd sync --once (mount config + auth as needed) |
The Docker image is a multi-arch manifest supporting linux/amd64 and linux/arm64 (AWS Graviton, Apple Silicon).
Requires Go 1.23+ to build from source.
Add SyncerD to your workflow:
- uses: clouddrove/syncerd@v1
with:
config: syncerd.yaml
once: "true"Add Docker credential steps (e.g. docker/login-action, aws-actions/amazon-ecr-login) before SyncerD so destination registries are authenticated.
Run SyncerD as a CronJob (stateless by default; no PVC):
helm install syncerd ./_helm/syncerd -n syncerd --create-namespaceSet config.destinations and config.images in values.yaml or via --set.
Credentials:
- Docker Hub (source): use
existingSecret(recommended) orsecret.*in values. - Destination registries (ECR/ACR/GCR/GHCR): create a Docker config secret and set
dockerConfigSecret. SyncerD automatically setsDOCKER_CONFIG=/var/lib/syncerd/.dockerso the credentials are found even when the pod runs as a non-root user. - To pull the SyncerD image itself from a private registry, use
imagePullSecrets— this is separate fromdockerConfigSecret.
ECR note: ECR tokens expire every 12 hours. Refresh dockerConfigSecret before expiry:
aws ecr get-login-password --region <region> | \
docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com
kubectl create secret generic syncerd-docker-config \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
-n syncerd --dry-run=client -o yaml | kubectl apply -f -See _helm/syncerd/README.md for all chart options.
syncerd sync --once # Run once and exit
syncerd sync # Run with built-in cron (from config)
syncerd sync --config /path/to/config.yamlFull example: syncerd.yaml.example.
| Section | Purpose |
|---|---|
source |
Docker Hub (username/password or token via env or config) |
destinations |
List of registries (ECR, ACR, GCR, GHCR); auth via Docker credential config |
images |
Images to sync; optional tags, watch_tags for new tag detection |
schedule |
Cron expression when running without --once |
state_path |
Optional state file for "already synced" tracking; leave empty for fully stateless |
slack |
Optional webhook; notify on new syncs and/or failures |
fail_fast |
true = stop on first error; false = best-effort per image/tag |
Override with SYNCERD_ prefix:
SYNCERD_SOURCE_USERNAME, SYNCERD_SOURCE_PASSWORD, SYNCERD_SOURCE_TOKEN,
SYNCERD_STATE_PATH, SYNCERD_SLACK_WEBHOOK_URL, SYNCERD_SLACK_CHANNEL,
SYNCERD_SLACK_MESSAGE_FORMAT, SYNCERD_FAIL_FAST.
- Docker Hub (source): Username/password or Personal Access Token (env or config). Credentials are validated at startup.
- Destinations (ECR/ACR/GCR/GHCR): SyncerD uses the default Docker keychain —
docker login, credential helpers, or GitHub Actions login steps. - ECR: Tokens expire every 12 hours. Ensure credentials are refreshed before each scheduled sync.
- Bugs & ideas: Open an issue.
- Code: See CONTRIBUTING.md. PRs welcome.
- Star the repo if SyncerD helps you — it helps others discover the project.
MIT. Built with go-containerregistry. Inspired by the need to work around Docker Hub rate limits.
