How to Use GitHub Actions + image-syncer for Automated Image Sync from Docker Hub to Azure ACR
Automating Image Sync from Docker Hub to Azure ACR
Section titled “Automating Image Sync from Docker Hub to Azure ACR”This article explains how to use GitHub Actions and the image-syncer tool to automate image synchronization from Docker Hub to Azure Container Registry, solving the problem of slow Docker Hub access in mainland China and some Azure regions, while improving image availability and deployment efficiency in Azure environments.
Background / Introduction
Section titled “Background / Introduction”The HagiCode project uses Docker images as its core runtime components, with the main images hosted on Docker Hub. As the project has evolved and Azure deployment needs have grown, we encountered the following pain points:
- Slow image pulls, because access to Docker Hub is limited in mainland China and some Azure regions
- Relying on a single image source creates a single point of failure risk
- Using Azure Container Registry in Azure environments provides better network performance and integration experience
To solve these problems, we need to establish an automated image synchronization mechanism that regularly syncs images from Docker Hub to Azure ACR, ensuring users get faster image pull speeds and higher availability in Azure environments.
About HagiCode
Section titled “About HagiCode”We are building HagiCode, an AI-driven coding assistant that makes development smarter, more convenient, and more enjoyable.
Smart: AI assistance throughout the entire process, from idea to code, boosting coding efficiency several times over. Convenient: Multi-threaded concurrent operations make full use of resources and keep the development workflow smooth. Fun: Gamification and an achievement system make coding less dull and more rewarding.
The project is evolving rapidly. If you are interested in technical writing, knowledge management, or AI-assisted development, welcome to check it out on GitHub.
Technical Solution Comparison
Section titled “Technical Solution Comparison”When defining the solution, we compared multiple technical approaches:
1. image-syncer (final choice)
Section titled “1. image-syncer (final choice)”- Incremental sync: only synchronizes changed image layers, significantly reducing network transfer
- Resume support: synchronization can resume after network interruptions
- Concurrency control: supports configurable concurrency to improve large image sync efficiency
- Robust error handling: built-in retry mechanism for failures (3 times by default)
- Lightweight deployment: single binary with no dependencies
- Multi-registry support: compatible with Docker Hub, Azure ACR, Harbor, and more
2. Docker CLI
Section titled “2. Docker CLI”- No incremental sync support: each run requires pulling the full image content
- Lower efficiency: large network transfer volume and longer execution time
- Simple and easy to use: relies on familiar
docker pull/docker pushcommands
3. Azure CLI
Section titled “3. Azure CLI”- Higher complexity: requires Azure CLI authentication setup
- Functional limitations:
az acr importis relatively limited - Native integration: integrates well with Azure services
Architecture Design Decisions
Section titled “Architecture Design Decisions”Decision 1: Set the sync frequency to daily at 00:00 UTC
Section titled “Decision 1: Set the sync frequency to daily at 00:00 UTC”- Balances image freshness with resource consumption
- Avoids peak business hours and reduces impact on other operations
- Docker Hub images are usually updated after daily builds
Decision 2: Sync all image tags
Section titled “Decision 2: Sync all image tags”- Maintains full consistency with Docker Hub
- Provides flexible version choices for users
- Simplifies sync logic by avoiding complex tag filtering rules
Decision 3: Store credentials in GitHub Secrets
Section titled “Decision 3: Store credentials in GitHub Secrets”- Natively supported by GitHub Actions with strong security
- Simple to configure and easy to manage and maintain
- Supports repository-level access control
Risk Assessment and Mitigation
Section titled “Risk Assessment and Mitigation”Risk 1: Azure ACR credential leakage
Section titled “Risk 1: Azure ACR credential leakage”- Use GitHub Secrets for encrypted storage
- Rotate ACR passwords regularly
- Limit ACR account permissions to push-only
- Monitor ACR access logs
Risk 2: Sync failures causing image inconsistency
Section titled “Risk 2: Sync failures causing image inconsistency”- image-syncer includes a built-in incremental sync mechanism
- Automatic retry on failure (3 times by default)
- Detailed error logs and failure notifications
- Resume support
Risk 3: Excessive resource consumption
Section titled “Risk 3: Excessive resource consumption”- Incremental sync reduces network transfer
- Configurable concurrency (
10in the current setup) - Monitor the number and size of synchronized images
- Run synchronization during off-peak hours
Core Solution
Section titled “Core Solution”We use an automated GitHub Actions + image-syncer solution to synchronize images from Docker Hub to Azure ACR.
Implementation Steps
Section titled “Implementation Steps”1. Preparation
Section titled “1. Preparation”- Create or confirm an Azure Container Registry in Azure Portal
- Create ACR access credentials (username and password)
- Confirm access permissions for the Docker Hub image repository
2. Configure GitHub Secrets
Section titled “2. Configure GitHub Secrets”Add the following secrets in the GitHub repository settings:
- AZURE_ACR_USERNAME: Azure ACR username
- AZURE_ACR_PASSWORD: Azure ACR password
3. Create the GitHub Actions workflow
Section titled “3. Create the GitHub Actions workflow”Configure the workflow in .github/workflows/sync-docker-acr.yml:
- Scheduled trigger: every day at 00:00 UTC
- Manual trigger: supports
workflow_dispatch - Extra trigger: run when the
publishbranch receives a push (for fast synchronization)
4. Workflow execution flow
Section titled “4. Workflow execution flow”| Sequence | Participant | Action | Description |
|---|---|---|---|
| 1 | GitHub Actions | Trigger workflow | Triggered by schedule, manual run, or a push to the publish branch |
| 2 | GitHub Actions → image-syncer | Download and run the sync tool | Enter the actual sync phase |
| 3 | image-syncer → Docker Hub | Fetch image manifests and tag list | Read source repository metadata |
| 4 | image-syncer → Azure ACR | Fetch existing image information from the target repository | Determine the current target-side state |
| 5 | image-syncer | Compare source and target differences | Identify image layers that need to be synchronized |
| 6 | image-syncer → Docker Hub | Pull changed image layers | Transfer only the content that needs updating |
| 7 | image-syncer → Azure ACR | Push changed image layers | Complete incremental synchronization |
| 8 | image-syncer → GitHub Actions | Return synchronization statistics | Includes results, differences, and error information |
| 9 | GitHub Actions | Record logs and upload artifacts | Useful for follow-up auditing and troubleshooting |
GitHub Actions Workflow Implementation
Section titled “GitHub Actions Workflow Implementation”Here is the actual workflow configuration in use (.github/workflows/sync-docker-acr.yml):
name: Sync Docker Image to Azure ACR
on: schedule: - cron: "0 0 * * *" # Every day at 00:00 UTC workflow_dispatch: # Manual trigger push: branches: [publish]
permissions: contents: read
jobs: sync: runs-on: ubuntu-latest
steps: - name: Checkout code uses: actions/checkout@v4
- name: Download image-syncer run: | # Download the image-syncer binary wget https://github.com/AliyunContainerService/image-syncer/releases/download/v1.5.5/image-syncer-v1.5.5-linux-amd64.tar.gz tar -zxvf image-syncer-v1.5.5-linux-amd64.tar.gz chmod +x image-syncer
- name: Create auth config run: | # Generate the authentication configuration file (YAML format) cat > auth.yaml <<EOF hagicode.azurecr.io: username: "${{ secrets.AZURE_ACR_USERNAME }}" password: "${{ secrets.AZURE_ACR_PASSWORD }}" EOF
- name: Create images config run: | # Generate the image synchronization configuration file (YAML format) cat > images.yaml <<EOF docker.io/newbe36524/hagicode: hagicode.azurecr.io/hagicode EOF
- name: Run image-syncer run: | # Run synchronization (using the newer --auth and --images parameters) ./image-syncer --auth=./auth.yaml --images=./images.yaml --proc=10 --retries=3
- name: Upload logs if: always() uses: actions/upload-artifact@v4 with: name: sync-logs path: image-syncer-*.log retention-days: 7Configuration Details
Section titled “Configuration Details”1. Trigger conditions
Section titled “1. Trigger conditions”- Scheduled trigger:
cron: "0 0 * * *"- runs every day at 00:00 UTC - Manual trigger:
workflow_dispatch- allows users to run it manually in the GitHub UI - Push trigger:
push: branches: [publish]- triggered when the publish branch receives a push (for fast synchronization)
2. Authentication configuration (auth.yaml)
Section titled “2. Authentication configuration (auth.yaml)”hagicode.azurecr.io: username: "${{ secrets.AZURE_ACR_USERNAME }}" password: "${{ secrets.AZURE_ACR_PASSWORD }}"3. Image sync configuration
Section titled “3. Image sync configuration”docker.io/newbe36524/hagicode: hagicode.azurecr.io/hagicodeThis configuration means synchronizing all tags from docker.io/newbe36524/hagicode to hagicode.azurecr.io/hagicode
4. image-syncer parameters
Section titled “4. image-syncer parameters”--auth=./auth.yaml: path to the authentication configuration file--images=./images.yaml: path to the image synchronization configuration file--proc=10: set concurrency to10--retries=3: retry failures3times
GitHub Secrets Configuration Checklist
Section titled “GitHub Secrets Configuration Checklist”Configure the following in Settings → Secrets and variables → Actions in the GitHub repository:
| Secret Name | Description | Example Value | How to Get It |
|---|---|---|---|
| AZURE_ACR_USERNAME | Azure ACR username | hagicode | Azure Portal → ACR → Access keys |
| AZURE_ACR_PASSWORD | Azure ACR password | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | Azure Portal → ACR → Access keys → Password |
Usage Instructions
Section titled “Usage Instructions”1. Manually trigger synchronization
Section titled “1. Manually trigger synchronization”- Open the
Actionstab of the GitHub repository - Select the
Sync Docker Image to Azure ACRworkflow - Click the
Run workflowbutton - Choose the branch and click
Run workflowto confirm
2. View synchronization logs
Section titled “2. View synchronization logs”- Click a specific workflow run record on the
Actionspage - View the execution logs for each step
- Download the
sync-logsfile from theArtifactssection at the bottom of the page
3. Verify synchronization results
Section titled “3. Verify synchronization results”# Log in to Azure ACRaz acr login --name hagicode
# List images and their tagsaz acr repository show-tags --name hagicode --repository hagicode --output tableNotes and Best Practices
Section titled “Notes and Best Practices”1. Security recommendations
Section titled “1. Security recommendations”- Rotate Azure ACR passwords regularly (recommended every 90 days)
- Use a dedicated ACR service account with push-only permissions
- Monitor ACR access logs to detect abnormal access in time
- Do not output credentials in logs
- Do not commit credentials to the code repository
2. Performance optimization
Section titled “2. Performance optimization”- Adjust the
--procparameter: tune concurrency based on network bandwidth (recommended5-20) - Monitor synchronization time: if it takes too long, consider reducing concurrency
- Clean up logs regularly: set a reasonable
retention-daysvalue (7days in the current setup)
3. Troubleshooting
Section titled “3. Troubleshooting”Issue 1: Authentication failed
Section titled “Issue 1: Authentication failed”Error: failed to authenticate to hagicode.azurecr.ioSolution:
- Check whether GitHub Secrets are configured correctly
- Verify whether the Azure ACR password has expired
- Confirm whether the ACR service account permissions are correct
Issue 2: Network timeout
Section titled “Issue 2: Network timeout”Error: timeout waiting for responseSolution:
- Check network connectivity
- Reduce concurrency (
--procparameter) - Wait for the network to recover and trigger the workflow again
Issue 3: Incomplete image synchronization
Section titled “Issue 3: Incomplete image synchronization”Warning: some tags failed to syncSolution:
- Check the synchronization logs to identify failed tags
- Manually trigger the workflow to synchronize again
- Verify that the source image on Docker Hub is working properly
4. Monitoring and alerts
Section titled “4. Monitoring and alerts”- Regularly check the
Actionspage to confirm workflow run status - Configure GitHub notifications to receive workflow failure alerts promptly
- Monitor Azure ACR storage usage
- Regularly verify tag consistency
Frequently Asked Questions and Solutions
Section titled “Frequently Asked Questions and Solutions”Q1: How do I sync specific tags instead of all tags?
Section titled “Q1: How do I sync specific tags instead of all tags?”Modify the images.yaml configuration file:
# Sync only the latest and v1.0 tagsdocker.io/newbe36524/hagicode:latest: hagicode.azurecr.io/hagicode:latestdocker.io/newbe36524/hagicode:v1.0: hagicode.azurecr.io/hagicode:v1.0Q2: How do I sync multiple image repositories?
Section titled “Q2: How do I sync multiple image repositories?”Add multiple lines in images.yaml:
docker.io/newbe36524/hagicode: hagicode.azurecr.io/hagicodedocker.io/newbe36524/another-image: hagicode.azurecr.io/another-imageQ3: How do I retry after a synchronization failure?
Section titled “Q3: How do I retry after a synchronization failure?”- Automatic retry: image-syncer includes a built-in retry mechanism (
3times by default) - Manual retry: click
Re-run all jobson the GitHub Actions page
Q4: How do I view detailed synchronization progress?
Section titled “Q4: How do I view detailed synchronization progress?”- View real-time logs on the
Actionspage - Download the
sync-logsartifact to see the full log file - The log file includes the synchronization status and transfer speed for each tag
Q5: How long does synchronization take?
Section titled “Q5: How long does synchronization take?”- Initial full synchronization: typically takes
10-30minutes depending on image size - Incremental synchronization: usually
2-5minutes if image changes are small - Time depends on network bandwidth, image size, and concurrency settings
Suggested Enhancements
Section titled “Suggested Enhancements”1. Add synchronization notifications
Section titled “1. Add synchronization notifications”Add a notification step to the workflow:
- name: Notify on success if: success() run: | echo "Docker images synced successfully to Azure ACR"2. Implement image tag filtering
Section titled “2. Implement image tag filtering”Add tag filtering logic to the workflow:
- name: Filter tags run: | # Sync only tags that start with v echo "docker.io/newbe36524/hagicode:v* : hagicode.azurecr.io/hagicode:v*" > images.yaml3. Add a synchronization statistics report
Section titled “3. Add a synchronization statistics report”- name: Generate report if: always() run: | echo "## Sync Report" >> $GITHUB_STEP_SUMMARY echo "- Total tags: $(grep -c 'synced' image-syncer-*.log)" >> $GITHUB_STEP_SUMMARY echo "- Sync time: ${{ steps.sync.outputs.duration }}" >> $GITHUB_STEP_SUMMARYConclusion
Section titled “Conclusion”With the method introduced in this article, we successfully implemented automated image synchronization from Docker Hub to Azure ACR. This solution uses the scheduled and manual trigger capabilities of GitHub Actions together with the incremental synchronization and error-handling features of image-syncer to ensure timely and consistent image synchronization.
We also discussed security best practices, performance optimization, troubleshooting, and other related topics to help users better manage and maintain this synchronization mechanism. We hope this article provides valuable reference material for developers who need to deploy Docker images in Azure environments.
References
Section titled “References”- HagiCode project GitHub repository
- image-syncer official documentation
- Azure Container Registry official documentation
- GitHub Actions official documentation
Reader Interaction
Section titled “Reader Interaction”Thank you for reading. If you found this article useful, please click the like button below 👍 so more people can discover it.
AI Assistance Disclosure
Section titled “AI Assistance Disclosure”This content was created with AI-assisted collaboration, reviewed by me, and reflects my own views and position.
Metadata
Section titled “Metadata”- Author: newbe36524
- Article URL: https://hagicode.com/blog/2026/01/25/how-to-sync-docker-hub-to-azure-acr-with-github
- Copyright: Unless otherwise stated, all articles on this blog are licensed under BY-NC-SA. Please include attribution when reprinting.