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:

  1. Authenticates Docker to the source ECR registry.
  2. Authenticates Docker to the destination ECR registry.
  3. Iterates through one or more repository:tag values.
  4. Copies each image from the source registry to the destination registry.
  5. Preserves the image tag.
  6. Preserves multi-architecture image manifests.
  7. 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.