cr.root.io. Once configured, your workloads pull Root images from your own ECR - reducing external network dependency, enabling lifecycle policies, and keeping image access within your AWS account.
What you will achieve
- A copy of selected
cr.root.ioimages stored in your AWS account in your ECR. - Your workloads pull from your ECR, reducing repeated external downloads from
cr.root.io. A safe approach for mutable tags (e.g.,:prod,:latest) where the mirroring job detects tag changes and updates your cache accordingly. - Optional architecture pinning (e.g., mirror only
linux/amd64) to avoid cross-platform surprises.
Prerequisites
You’ll need:- Your AWS account ID and AWS region(s) where you want the cache (ECR is regional).
- Credentials for pulling from
cr.root.io(username/password/token you already use). - A place to run a small scheduled job (anything that can run a script on a timer).
- Docker installed (the mirroring flow uses
docker pull/tag/push).
AWS authentication (required)
The mirroring job needs AWS credentials. Use either: Option A: AWS profileAWS_REGION in either case.)
Step 1 — Choose a destination naming convention in ECR
Pick a prefix in your ECR, for example:root-mirror/
Example mapping:
- Source:
cr.root.io/team/app:prod - Destination:
<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/root-mirror/team/app:prod
Step 2 — Create the mirror repositories in ECR
You can either:- Pre-create the repositories you plan to mirror into (recommended for tighter IAM + predictability), or
- Allow the mirroring job to auto-create repositories (simpler, requires additional permission).
Option A (recommended): Pre-create repositories
For each source repo path (everything aftercr.root.io/ up to :tag), create a matching ECR repo under your chosen prefix.
Example:
- Source:
cr.root.io/team/app:prod - Create ECR repo:
root-mirror/team/app
Option B: Auto-create repositories from the script
If you prefer not to pre-create repos, the script can create them as needed. If you choose this, ensure the job role has:ecr:CreateRepositoryecr:DescribeRepositories
Step 3 — Ensure your mirroring job can push to ECR
Your job needs permission to push images into ECR. Minimum AWS permissions commonly required:ecr:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:InitiateLayerUploadecr:UploadLayerPartecr:CompleteLayerUploadecr:PutImage- (optional)
ecr:CreateRepository+ecr:DescribeRepositories(if auto-creating repos)
root-mirror/* repositories for least privilege.
Step 4 — Define what to mirror (images.txt format)
Create animages.txt file.
Format
Each line is:- The first column is required:
cr.root.io/...:tag - The second column is optional: architecture such as
amd64orarm64- If provided, the script treats it as
linux/<arch>(it prependslinux/internally)
- If provided, the script treats it as
Example images.txt
arch column exists:
- If you run the job from macOS (darwin/arm64) or a different architecture, you often still want to mirror
linux/amd64. - Explicit arch avoids pulling the “wrong” platform variant (or failing to resolve).
Step 5 — Mirror images (with tag-change detection and retries)
What the script does (behavior)
The mirroring script is designed to be safe for mutable tags and reliable across environments:- Cross-platform logging: uses a portable UTC timestamp format (works on Linux + macOS).
- Retries: wraps network-sensitive operations (especially
docker push) with retries (configurable attempts + delay). - Architecture support: reads optional
archfromimages.txtand mirrorslinux/<arch>when specified. - Change detection: avoids re-copying images unless the content changed:
- It compares image config digests (SHA256 of the image config JSON), which is stable across OCI vs Docker v2 manifest format differences.
- This prevents false-positive “changed” loops that can happen when only manifest formatting differs.
How tag-change detection works (in plain terms)
For each<image>:<tag> [arch]:
- Resolve the current config digest for the source tag in
cr.root.io(optionally for a specific platform). - Resolve the current config digest for the same tag in your ECR mirror repo (same platform).
- If digests match → skip (already cached).
- If digests differ or destination tag is missing → pull, tag, and push to ECR.
5.1 Create this mirroring script
Save asmirror_to_ecr.sh:
5.2 Running the script
Export required environment variables:Step 6 — Update workloads to pull from your ECR
Change pulls from:cr.root.io/team/app:prod
<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/root-mirror/team/app:prod
Step 7 - Suggested scheduling (platform-agnostic)
The mirroring script is designed to be run repeatedly. Pick a cadence based on how often your mutable tags change (for example: every 15–60 minutes for frequently-changing tags, less often for stable version tags). You can schedule it in any environment that can run a command on a timer:- Cron on a VM/bastion/utility host
- Install Docker + AWS CLI, store
images.txt+mirror_to_ecr.sh, then add a cron entry. - Example (runs every 30 minutes):
- Install Docker + AWS CLI, store
-
CI scheduler
- Most CI systems support scheduled workflows/pipelines.
- Store
images.txtin the repo, inject secrets (ROOT_USER/ROOT_PASS, AWS creds/profile) via the CI secret manager, run./mirror_to_ecr.sh.
-
A scheduled container/job runner
- Package the script +
images.txtinto a small container image (or mount them), then run it periodically using whatever scheduler you already use (Kubernetes CronJob, a container task scheduler, etc.). - Make sure Docker is available (either inside the job image, or via a Docker-enabled runner).
- Package the script +
images.txt focused on the specific images/tags your production workloads actually use.
Networking note (optional)
If your workloads run in private subnets, consider configuring ECR VPC endpoints / PrivateLink so pulls can stay private and reduce NAT usage. The exact endpoint setup depends on your VPC architecture.Troubleshooting
- 401/403 pulling from
cr.root.io: credentials/token expired or not authorized - Denied pushing to ECR: IAM role missing
ecr:PutImageor upload permissions - Repository not found: either pre-create repos (Step 2 Option A) or set
AUTO_CREATE_REPOS=true - Wrong architecture mirrored: add the second column (
amd64/arm64) inimages.txt - Intermittent push failures: retries are built-in; if persistent, check network throttling / ECR limits and increase retry settings (attempts/delay)