Migrating Amazon ECR Images Between AWS Accounts with a Simple Bash Script
Moving container images between AWS accounts is a common task during platform migrations, account restructuring, environment promotion, mergers, acquisitions, or when separating production and non-production workloads.
Amazon Elastic Container Registry, or Amazon ECR, makes it easy to store and distribute container images inside AWS. But when you need to copy images from one AWS account to another, especially across multiple repositories and tags, the process can become repetitive.
To make this easier, I created a Bash script that migrates ECR images from a source AWS account to a destination AWS account.
GitHub repository: https://github.com/ilich/aws-samples/tree/main/ecr/migrate-repositories
The script lets you copy one or more repository:tag images from one ECR registry to another while preserving the repository name and tag. It also supports multi-platform images, such as images built for both linux/amd64 and linux/arm64.
What the Script Does
The script migrates container images between two Amazon ECR registries.
At a high level, it:
- Authenticates Docker to the source ECR registry.
- Authenticates Docker to the destination ECR registry.
- Iterates through one or more
repository:tagvalues. - Copies each image from the source registry to the destination registry.
- Preserves the image tag.
- Preserves multi-architecture image manifests.
- Reports any failed image copies at the end.
This is useful when you want to move images between AWS accounts without manually pulling, tagging, and pushing each image one by one.
Why This Is Useful
A traditional image migration workflow often looks like this:
docker pull source-account.dkr.ecr.region.amazonaws.com/myapp:latest
docker tag source-account.dkr.ecr.region.amazonaws.com/myapp:latest destination-account.dkr.ecr.region.amazonaws.com/myapp:latest
docker push destination-account.dkr.ecr.region.amazonaws.com/myapp:latest
That works, but it has several drawbacks.
First, it requires local disk space because the image layers are pulled to your machine. Second, it can be slower for large images. Third, it may not preserve multi-platform manifests correctly if you only pull a single platform image locally. Finally, it becomes tedious when you need to migrate many images.
The script avoids this by using Docker Buildx imagetools create, which copies the image manifest directly between registries. This means the image does not need to be pulled to local disk first.
Prerequisites
Before using the script, you need the following installed and configured:
- AWS CLI v2
- Docker
- Docker Buildx
You also need AWS CLI profiles configured for both the source and destination accounts.
The source AWS account needs permissions to read from ECR, including:
- ecr:GetAuthorizationToken
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
The destination AWS account needs permissions to write to ECR, including:
- ecr:GetAuthorizationToken
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
- ecr:PutImage
The target repositories must already exist in the destination account. The script does not create ECR repositories automatically.
For example, if you are migrating:
myapp:latest
from the source account, then the destination account must already have an ECR repository named:
myapp
Basic Usage
The script uses the following syntax:
./migrate-ecr-images.sh [OPTIONS] -s SOURCE_ACCOUNT -d DEST_ACCOUNT -r REGION repo:tag [repo:tag ...]
Required arguments:
-s SOURCE_ACCOUNT AWS account ID of the source account
-d DEST_ACCOUNT AWS account ID of the destination account
-r REGION AWS region, for example us-east-1
repo:tag One or more repository and tag pairs to migrate
Optional arguments:
--src-profile PROFILE AWS CLI profile for the source account
--dst-profile PROFILE AWS CLI profile for the destination account
-h, --help Show help
If no profiles are provided, the script uses the default AWS CLI profile for both accounts.
Example: Migrating a Single Image
To migrate a single image using the default AWS CLI profile, run:
./migrate-ecr-images.sh \
-s 111122223333 \
-d 444455556666 \
-r us-east-1 \
myapp:latest
This copies:
111122223333.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
to:
444455556666.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
Example: Migrating Multiple Images
You can also migrate multiple images in one command:
./migrate-ecr-images.sh \
-s 111122223333 \
-d 444455556666 \
-r us-east-1 \
myapp:latest myapp:v1.2.3 worker:v3.1
The script processes each image independently. If one image fails, the script records the failure and continues processing the remaining images.
Example: Using Named AWS Profiles
In many real-world environments, you will use different AWS CLI profiles for each account.
For example:
./migrate-ecr-images.sh \
-s 111122223333 --src-profile prod-readonly \
-d 444455556666 --dst-profile staging-admin \
-r us-east-1 \
myapp:latest myapp:v1.2.3 worker:v3.1
In this example, prod-readonly is used to authenticate to the source account, and staging-admin is used to authenticate to the destination account.
This is especially helpful when migrating from production to staging, from a legacy AWS account to a new landing zone account, or from one organizational unit to another.
Why Docker Buildx?
The key part of the script is Docker Buildx, specifically:
docker buildx imagetools create --tag "$dst_image" "$src_image"
I used Docker Buildx because it allows the script to copy the image reference from the source registry to the destination registry without doing a traditional local pull, tag, and push workflow.
This has a few important benefits.
First, it avoids unnecessary local disk usage. The image does not need to be downloaded to the machine running the script before it is pushed to the destination registry.
Second, it is better suited for multi-platform images. Many modern container images support more than one CPU architecture, for example linux/amd64 and linux/arm64. A regular docker pull may pull only the platform-specific image for the local machine. With docker buildx imagetools create, the manifest list can be copied as-is, preserving the multi-architecture image in the destination registry.
Third, it keeps the migration workflow simple. The script only needs to authenticate to both ECR registries and ask Docker Buildx to create the destination image reference from the source image reference.
This makes the script lightweight, repeatable, and practical for moving multiple ECR images between AWS accounts.
Repository Naming Requirements
The script preserves repository names.
That means this source image:
source-account.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
is copied to:
destination-account.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
The repository name remains:
myapp
The tag remains:
latest
Because of this, the destination ECR repository must already exist with the same name. If the destination repository does not exist, the image copy will fail.
This design keeps the script focused on image migration only. Repository creation, lifecycle policies, scanning configuration, encryption settings, and access policies can be managed separately using Terraform, AWS CDK, CloudFormation, or another infrastructure-as-code tool.
When to Use This Script
This script is useful for scenarios such as:
- Migrating images from one AWS account to another
- Promoting images between isolated AWS accounts
- Moving workloads into a new AWS organization or landing zone
- Copying production images into a staging or disaster recovery account
- Preserving multi-architecture images during migration
- Batch-copying multiple tagged images
It is especially helpful when you want a simple, repeatable command-line workflow without introducing a larger migration tool.
Things to Keep in Mind
There are a few important considerations before running the script.
First, the destination repositories must already exist. The script does not create missing repositories.
Second, the repository names are preserved. If you need to rename repositories during migration, you would need to modify the script.
Third, the script works within a single AWS region per execution. If you need to migrate images across regions, run the script with the appropriate region or extend it to support separate source and destination regions.
Fourth, make sure both AWS profiles have the required ECR permissions. Source access must be able to read image manifests and layers. Destination access must be able to write image manifests and upload layers.
Finally, validate migrated images before updating production workloads to consume them from the new account.
Conclusion
Migrating ECR images between AWS accounts does not need to be complicated.
This Bash script provides a simple way to copy one or more tagged images from a source AWS account to a destination AWS account. It authenticates to both registries, preserves repository names and tags, supports multi-platform images, and avoids local image pull and push operations by using Docker Buildx imagetools create.
For teams working with multi-account AWS environments, this provides a lightweight and repeatable approach for ECR image migration.