Amazon ECR Explained: Private vs Public, Push/Pull, IAM, and Scanning
Amazon ECR (Elastic Container Registry) is AWS’s managed service for storing and serving container images. You push a Docker image to ECR, and services like ECS, EKS, Lambda, and App Runner can pull it directly using their IAM roles, with no separate registry credentials, no pull rate limits, and no cross-region traffic to a third-party registry.
This page covers what ECR is, how it fits into the AWS container ecosystem, how the push/pull workflow works, and what IAM access control and image scanning actually do. For production hardening (tag immutability, VPC endpoints, cross-account setups, and lifecycle policy design), see the Amazon ECR Best Practices for Production guide.
Simple explanation
ECR is AWS’s place to store container images so services like ECS, EKS, Lambda, and App Runner can pull them. Think of it as a private warehouse for the packaged software bundles your workloads run from.
Think of ECR like a library system. The registry is the library building itself. Each repository is a labelled shelf for one subject (one application). Each image is a book on that shelf. A tag is a sticky note on the cover saying “edition 1.2.3” — but someone could move that note to a different book. A digest is the book’s unique ISBN: it permanently identifies exactly that edition and cannot be reassigned.
The five terms in full:
- Registry: the top-level storage system. Every AWS account gets one private ECR registry, identified by your account ID and region:
123456789012.dkr.ecr.us-east-1.amazonaws.com. - Repository: a named slot inside your registry for one application or image family, such as
my-apporapi-server. You create repositories and push images into them. - Image: the packaged, immutable bundle your container runs from. An image includes your application code, runtime, libraries, and dependencies. See Container Images in AWS for a deeper look at how images are built and layered.
- Tag: a human-readable label pointing to a specific image, such as
1.2.3,latest, orabc1234(a Git commit SHA). Tags are mutable by default. The same tag can be reassigned to a different image. - Digest: an immutable SHA256 fingerprint of the exact image content:
sha256:abc123…. If you need to guarantee you are running exactly the same bits every time, not just the same tag, reference the image by digest.
Full image reference format: account-id.dkr.ecr.region.amazonaws.com/repository-name:tag
What Amazon ECR is
ECR is a fully managed registry. AWS handles availability, replication, and storage. You do not run any registry infrastructure. You create repositories, push images, and reference them by URI. AWS compute services pull from ECR automatically using their IAM execution roles.
Teams use ECR for AWS workloads rather than an external registry for several concrete reasons:
- IAM-based access. No separate registry account or password file. An ECS task, Lambda function, or EKS node pulls from ECR using its IAM role. You control access the same way you control any other AWS resource.
- Regional proximity. ECR repositories live in a specific AWS region. Pulling from a repo in the same region as your workload is fast, and there are no data transfer fees for pulls within the same region.
- Image scanning. Basic vulnerability scanning is included at no extra cost. Enhanced scanning via Amazon Inspector adds continuous scanning and language-level dependency checks.
- Lifecycle policies. Automatically expire old images to keep storage costs under control. Without this, a busy CI/CD pipeline accumulates thousands of images that are never cleaned up.
- No pull rate limits. Docker Hub throttles unauthenticated and free-tier pulls. ECR has no equivalent limits for AWS traffic pulling from within the same region.
How ECR works
The end-to-end flow from build to deployed workload:
- Build the image. A developer or CI/CD pipeline builds a container image from a Dockerfile. If you use AWS CodeBuild to build Docker images, the build runs inside AWS and pushes directly to ECR without leaving the AWS network.
- Tag the image. Assign a version tag that identifies this specific build: a semantic version (
1.2.3) or a Git commit SHA (abc1234). Tagging withlatestalone loses rollback ability. - Authenticate Docker to ECR. The build environment runs
aws ecr get-login-passwordto get a short-lived token, then passes it todocker login. This token is valid for 12 hours. - Push the image.
docker pushsends image layers to your ECR repository. Only changed layers are uploaded. Unchanged layers are stored once and reused across images. - AWS service pulls the image. When ECS launches a task, EKS schedules a pod, Lambda initialises a container function, or App Runner deploys a service, it fetches the image from ECR using the service’s IAM execution role.
- ECR scans the image. If scan-on-push is enabled, ECR checks the image against known vulnerability databases immediately after the push completes.
- Lifecycle rules expire old images. Lifecycle policies run periodically and delete images that match expiry criteria. For example: untagged images older than 7 days, or more than 10 release-tagged images in a repository.
When to use Amazon ECR
Use ECR when:
- Running containers on ECS or EKS. ECR is the natural image store for both. Task definitions and pod specs reference ECR image URIs directly. The service’s IAM role handles authentication automatically. See EKS vs ECS in AWS if you are still deciding between the two.
- Deploying Lambda container images. Lambda functions packaged as container images must use ECR as their image source. If your function exceeds the zip deployment size limit or needs a custom runtime, Lambda container images pull from ECR at cold start.
- Deploying to App Runner. AWS App Runner can pull container images from ECR and deploy them as managed web services with no infrastructure to manage.
- Keeping CI/CD builds inside AWS. When your pipeline runs inside AWS (CodeBuild, GitHub Actions with OIDC), pushing to ECR instead of Docker Hub avoids pull rate limits and keeps image traffic inside the AWS network. See CI/CD Pipelines for Amazon ECS for an end-to-end example.
- Sharing public base images. ECR Public lets you publish open-source or publicly shared images without a Docker Hub account. AWS uses ECR Public for its own official images.
- Enforcing access control on images. For proprietary application images, ECR’s IAM-based access means only the identities you explicitly permit can pull them.
If you are still deciding which AWS compute service to run your containers on, Choosing Between EC2, Lambda, and Containers in AWS walks through the trade-offs.
ECR Private vs ECR Public
| ECR Private | ECR Public | |
|---|---|---|
| Registry URL | account-id.dkr.ecr.region.amazonaws.com | public.ecr.aws |
| Auth to pull | Required (IAM credentials) | Not required |
| Auth to push | Required | Required (AWS credentials) |
| Visibility | Private to your account (or accounts you share with) | Publicly visible; browsable at gallery.ecr.aws |
| Typical use | Production workloads, proprietary application images | Open-source images, public base images, AWS official images |
| Region scope | Per-region | Global (hosted in us-east-1, replicated globally) |
Most teams use private ECR for their application images and ECR Public only for open-source projects or base images they want to share externally.
Amazon ECR vs Docker Hub
| Amazon ECR (Private) | Docker Hub | |
|---|---|---|
| Primary purpose | Private registry for AWS-native workloads | Public registry and community image hub |
| Auth model | IAM roles and policies | Docker Hub account credentials |
| AWS service integration | Native. ECS, EKS, Lambda, and App Runner use their execution roles automatically. | Manual. You must configure pull secrets or credentials separately. |
| Pull rate limits | None for AWS traffic in the same region | Yes. Throttled for unauthenticated and free-tier pulls. |
| Vulnerability scanning | Built in (basic free, enhanced via Inspector) | Available on paid plans |
| Best fit | Proprietary images deployed on AWS | Public open-source images; base images for local development |
Most AWS teams pull well-known base images (such as ubuntu, node, or python) from Docker Hub or ECR Public during development, build their own application image on top, and store that in private ECR.
Creating a repository
Create a private ECR repository with the AWS CLI:
aws ecr create-repository \
--repository-name my-app \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256 \
--region us-east-1The command returns the full repository URI you will use for all push and pull operations:
{
"repository": {
"repositoryArn": "arn:aws:ecr:us-east-1:123456789012:repository/my-app",
"registryId": "123456789012",
"repositoryName": "my-app",
"repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app"
}
}scanOnPush=true enables automatic basic scanning on every push. encryptionType=AES256 encrypts images at rest using an AWS-managed key. For customer-managed KMS keys, use encryptionType=KMS and specify a key ARN.
Pushing and pulling images
Authenticate Docker to ECR
ECR authentication works like a visitor day pass at an office building. You show your AWS credentials at the front desk, get a pass that is valid until midnight, and can come and go freely until it expires. After 12 hours the pass stops working — you need to get a new one. In CI/CD pipelines, this means re-authenticating at the start of each run, not once when you set up the pipeline.
The AWS CLI generates the token and pipes it directly to Docker:
aws ecr get-login-password --region us-east-1 | \
docker login \
--username AWS \
--password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.comRun aws ecr get-login-password at the top of every pipeline job, not once during setup. ECR tokens expire after 12 hours. A pipeline that authenticates at configuration time will silently fail when the token expires mid-run.
Tag and push
# Build the image
docker build -t my-app:1.2.3 .
# Tag with the full ECR repository URI
docker tag my-app:1.2.3 \
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:1.2.3
# Push
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:1.2.3Version tags, latest, and digests
The latest tag is convenient but problematic for production deployments. Because it is mutable, two separate deploys using latest may actually run different code. Use a specific version tag (a semantic version like 1.2.3, or a Git commit SHA like abc1234) so you can trace exactly which build is running and roll back to a known-good version if needed.
If you need an even stronger guarantee, reference the image by its SHA256 digest:
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app@sha256:abc123….
Digests are immutable. No one can change what they point to. Enabling tag immutability on the repository is the more practical production pattern and is covered in the ECR Best Practices for Production guide.
Pull an image
docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:1.2.3List images in a repository
aws ecr list-images \
--repository-name my-app \
--query "imageIds[*].[imageTag,imageDigest]" \
--output tableImage scanning
ECR scans images for known vulnerabilities (CVEs) in two modes:
Basic scanning (free)
Basic scanning checks OS package vulnerabilities against standard CVE databases. It runs automatically on push when enabled, or on demand. Results are categorised as CRITICAL, HIGH, MEDIUM, LOW, and INFORMATIONAL. Enable it on every repository. It is free and requires no ongoing configuration beyond turning it on.
# Trigger a manual scan (if not using scan-on-push)
aws ecr start-image-scan \
--repository-name my-app \
--image-id imageTag=1.2.3
# View scan results
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=1.2.3Enhanced scanning (paid, via Amazon Inspector)
Enhanced scanning uses Amazon Inspector and goes further than basic scanning in two ways:
- It checks both OS packages and application-level dependencies: Python pip packages, npm packages, Java libraries, and others.
- It scans continuously. If a new CVE is published against a library your image uses, you are notified without pushing a new image.
Enhanced scanning is a paid feature. Basic scanning is the right starting point for most teams.
A HIGH-severity finding does not automatically mean your application is exploitable. The vulnerable code path may not be reachable in your specific deployment. Scanning tells you what known vulnerabilities exist in the image’s packages. It does not assess exploitability, application logic, or secrets accidentally baked into the image. Treat a scan as one input into a security review, not a deployment gate on its own.
IAM access control
ECR access is controlled by two distinct types of IAM policy. Understanding which to use matters. See IAM Roles Explained for a primer on how IAM roles and policies work in AWS.
IAM identity policies (attached to roles or users)
These are the most common case. Attach an identity policy to an IAM role to grant it permission to push or pull ECR images: for example, an ECS task execution role, a Lambda execution role, or a CI/CD pipeline role.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "arn:aws:ecr:us-east-1:123456789012:repository/my-app"
}
]
}The AmazonEC2ContainerRegistryReadOnly AWS managed policy is a convenient shortcut for pull-only access. For the difference between AWS managed policies and customer-managed policies, see Managed vs Customer Managed Policies.
Repository resource policies (cross-account access)
A repository policy is attached directly to a specific ECR repository and grants access to principals in other AWS accounts. Use this when a workload in Account B needs to pull images stored in Account A’s ECR registry. Identity policies alone cannot grant cross-account ECR access.
aws ecr set-repository-policy \
--repository-name my-app \
--policy-text '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::987654321098:root"},
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
]
}]
}'Within a single AWS account, identity policies on roles are all you need. Repository policies exist specifically for cross-account pull scenarios, where a role in another account needs access to your images.
Integration with AWS compute services
AWS compute services authenticate to ECR automatically using the service’s IAM execution role. You provide the image URI and ECR handles the credential exchange.
| Service | How you reference the image | Role that needs pull access | Notes |
|---|---|---|---|
| ECS | image field in a container definition inside a task definition | Task execution role | Attach AmazonEC2ContainerRegistryReadOnly to the task execution role |
| EKS | Image field in a Kubernetes pod spec or Deployment manifest | Node IAM role (or an IRSA role for finer-grained control) | See Deploying Containers with kubectl on EKS for pod spec examples |
| Lambda | ImageUri in create-function or update-function-code | Lambda execution role | Image must be in the same region as the Lambda function |
| App Runner | ECR image URI in the App Runner service configuration | App Runner access role | App Runner requires an access role with ECR pull permissions at service creation time |
For ECS and Lambda, attaching AmazonEC2ContainerRegistryReadOnly to the execution role covers all required pull actions. For EKS, the same policy is attached to the EC2 instance profile on the node group.
Common mistakes
Pushing only to latest and never setting a lifecycle policy. Two weeks later: you cannot identify which build is deployed, and you have 500 images in ECR accumulating storage costs with no way to clean them up automatically. Both are easy to prevent at setup time.
- Relying only on the latest tag.
latestis mutable and tells you nothing about which version is actually deployed. Tag every image with a version number or Git commit SHA so you can trace deployments and roll back to a specific build. - No lifecycle policy. Without one, images accumulate indefinitely. A pipeline pushing 10 images a day creates over 3,000 images in a year. Set lifecycle policies on every repository at creation time, before costs build up.
- Not re-authenticating in CI/CD pipelines. ECR tokens expire after 12 hours. A pipeline that authenticated once at setup time will fail hours later with an auth error. Re-run
aws ecr get-login-passwordat the start of each pipeline run. - Storing images in the wrong region. ECR repositories are regional. If your ECS cluster is in
eu-west-1but your images are inus-east-1, every pull crosses regions: slower, and subject to data transfer costs. Create ECR repositories in the same region as your workloads. - Confusing repository policies and IAM identity policies. Identity policies are attached to roles or users and control what those identities can do across ECR. Repository policies are attached to a specific repository and control who can access it, including principals in other accounts. Within a single account, identity policies are all you need.
- Assuming a clean scan means the image is safe to deploy. Scanning reports known CVEs in packaged dependencies. It does not assess application logic, misconfigurations, secrets baked into the image, or whether a vulnerable code path is actually reachable. A scan result is one data point, not a deployment gate.
Summary
- ECR is AWS’s managed container registry. Private repositories are secured by IAM. ECR Public at
public.ecr.awsis for sharing images publicly without authentication. - The core concepts: registry (account-level), repository (per-application), image (the packaged bundle), tag (mutable label), and digest (immutable SHA256 fingerprint).
- Authenticate Docker to ECR with
aws ecr get-login-password. The token lasts 12 hours. Re-authenticate at the start of every CI/CD run. - Tag images with version numbers or commit SHAs, not just
latest. You cannot roll back to a specific version if you have no specific version tags. - ECS, EKS, Lambda, and App Runner pull from ECR using the service’s IAM execution role. No separate credentials needed.
- Enable basic scanning on every repository (free) and set lifecycle policies before images accumulate.
- For production hardening (tag immutability, VPC endpoints, cross-account pulls), see Amazon ECR Best Practices for Production.
Frequently asked questions
What is Amazon ECR used for?
Amazon ECR (Elastic Container Registry) is AWS's managed container image registry. Teams use it to store, version, and serve container images to AWS services like ECS, EKS, Lambda, and App Runner. It provides IAM-based access control, built-in vulnerability scanning, and lifecycle policies to automatically clean up old images.
Is Amazon ECR private or public?
ECR supports both. Every AWS account gets a private registry secured by IAM. Only authenticated principals with the right permissions can push or pull. ECR Public is a separate registry at public.ecr.aws for sharing images with no authentication required. Most production workloads use private ECR.
How is ECR different from Docker Hub?
Docker Hub is the default public registry for Docker images. ECR is designed for AWS-native workloads. IAM-based auth means ECS, EKS, and Lambda can pull images using their execution roles without separate credentials. Pulling from ECR within the same region is also faster and avoids Docker Hub pull rate limits.
Do I need ECR for ECS, EKS, or Lambda?
No. These services can pull from any accessible registry. But ECR is the most practical choice for AWS workloads: no separate credentials to manage, no pull rate limits, no cross-region data transfer fees, and image scanning built in.
How do I authenticate Docker to ECR?
Run: aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com. The token is valid for 12 hours. In CI/CD pipelines, re-authenticate at the start of each run.