AWS for DevOps — ECR: Elastic Container Registry
7.2 — ECR: Elastic Container Registry
Amazon ECR is a fully managed Docker container registry. It stores, manages, and deploys your Docker images — tightly integrated with ECS, EKS, CodeBuild, and Lambda.
Why ECR Over Docker Hub?
| Feature | Docker Hub (Free) | ECR |
|---|---|---|
| Private repos | 1 | Unlimited |
| Pull rate limits | 100/6h (anonymous) | No limits within AWS |
| IAM integration | No | Yes — fine-grained access |
| Vulnerability scanning | Basic | Enhanced (Inspector) |
| Image signing | No | Sigstore support |
| Encryption | No | AES-256 or KMS |
Creating a Repository
$ aws ecr create-repository \
--repository-name my-app \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256
{
"repository": {
"repositoryArn": "arn:aws:ecr:ap-south-1:123456789012:repository/my-app",
"repositoryUri": "123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-app",
"imageScanningConfiguration": { "scanOnPush": true }
}
}
Push & Pull Workflow
Developer / CI ECR
┌───────────────┐ ┌─────────┐
│ docker build │ │ my-app │
│ docker tag │───── docker push──▶│ :v1 │
│ │ │ :v2 │
└───────────────┘ │ :latest│
└─────────┘
│
ECS / EKS / Lambda ◀──── docker pull ─────┘
Authenticate and Push
# Step 1 — Authenticate Docker to ECR
$ aws ecr get-login-password --region ap-south-1 \
| docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-south-1.amazonaws.com
Login Succeeded
# Step 2 — Build the image
$ docker build -t my-app:v1 .
# Step 3 — Tag for ECR
$ docker tag my-app:v1 123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-app:v1
$ docker tag my-app:v1 123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-app:latest
# Step 4 — Push
$ docker push 123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-app:v1
$ docker push 123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-app:latest
Listing Images
$ aws ecr list-images --repository-name my-app
{
"imageIds": [
{ "imageDigest": "sha256:abc123...", "imageTag": "v1" },
{ "imageDigest": "sha256:abc123...", "imageTag": "latest" },
{ "imageDigest": "sha256:def456...", "imageTag": "v2" }
]
}
# Get image details
$ aws ecr describe-images --repository-name my-app \
--query 'imageDetails[].{Tag:imageTags[0],Size:imageSizeInBytes,Pushed:imagePushedAt}' \
--output table
Vulnerability Scanning
# Scan an image manually
$ aws ecr start-image-scan \
--repository-name my-app \
--image-id imageTag=v1
# Get scan results
$ aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=v1 \
--query 'imageScanFindings.findingSeverityCounts'
{
"CRITICAL": 0,
"HIGH": 2,
"MEDIUM": 5,
"LOW": 12,
"INFORMATIONAL": 8
}
Lifecycle Policies
Lifecycle policies automatically clean up old images to save storage costs:
$ aws ecr put-lifecycle-policy \
--repository-name my-app \
--lifecycle-policy-text '{
"rules": [
{
"rulePriority": 1,
"description": "Keep only last 10 tagged images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 10
},
"action": { "type": "expire" }
},
{
"rulePriority": 2,
"description": "Remove untagged images older than 7 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 7
},
"action": { "type": "expire" }
}
]
}'
Repository Policies (Cross-Account Access)
Allow another AWS account to pull images:
$ aws ecr set-repository-policy \
--repository-name my-app \
--policy-text '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPullFromAccount2",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::987654321098:root" },
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
]
}
]
}'
Image Immutability
Prevent tags from being overwritten (good practice for production):
$ aws ecr put-image-tag-mutability \
--repository-name my-app \
--image-tag-mutability IMMUTABLE
With immutability enabled:
docker push my-app:v1works the first timedocker push my-app:v1again → fails (tag already exists)- Forces unique tags like
v1.0.1,v1.0.2, or Git commit SHA
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| No lifecycle policy | Storage costs grow forever | Set a policy to expire old images |
Using :latest only |
Can’t track which version is deployed | Tag with version or Git SHA |
| Auth token expired | Push/pull fails after 12 hours | Re-run aws ecr get-login-password |
| No vulnerability scanning | Deploying images with known CVEs | Enable scanOnPush=true |
Quick Reference
| Task | Command |
|---|---|
| Create repository | aws ecr create-repository --repository-name NAME |
| Authenticate | aws ecr get-login-password \| docker login --username AWS --password-stdin ACCOUNT.dkr.ecr.REGION.amazonaws.com |
| Push image | docker push ACCOUNT.dkr.ecr.REGION.amazonaws.com/NAME:TAG |
| List images | aws ecr list-images --repository-name NAME |
| Delete image | aws ecr batch-delete-image --repository-name NAME --image-ids imageTag=TAG |
| Set lifecycle policy | aws ecr put-lifecycle-policy --repository-name NAME --lifecycle-policy-text '...' |
Next → ECS Task Definitions