What Is AWS ECR?
The Need for a Private Registry
When you build a Docker image locally, it only exists on your machine. To run it on EC2, ECS, or anywhere else, you need to:
- Push the image to a registry
- Pull it from the registry on the target machine
Docker Hub is the public default. It's fine for open-source projects, but for production apps you want a private registry — one that requires authentication and doesn't expose your code publicly.
Amazon ECR is AWS's managed private container registry. It integrates natively with IAM (no separate credentials to manage for EC2/ECS), is physically close to your AWS resources for fast pulls, and costs $0.10/GB/month for storage.
Registry and Repository Structure
AWS Account
└── ECR Registry (one per account per region)
├── Repository: my-app/api (stores versions of your API image)
│ ├── Tag: latest
│ ├── Tag: v1.2.0
│ └── Tag: v1.1.0
└── Repository: my-app/worker (stores versions of your worker image)
└── Tag: latest
Each repository stores one image with multiple tags. The full URI of an image looks like:
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:v1.2.0
└─ account id ─┘ └─ region ─┘ └── repository ──┘└─ tag ─┘
Creating a Repository
In the AWS console: ECR → Create repository → Private → enter name
Or with the AWS CLI:
aws ecr create-repository \
--repository-name my-app/api \
--region us-east-1
Authenticating Docker to ECR
Before you can push or pull, Docker needs to authenticate with ECR. ECR uses temporary tokens (valid 12 hours) generated by the AWS CLI:
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
This pipes a temporary password directly to docker login. You'll see Login Succeeded.
Pushing an Image
# 1. Build your image
docker build -t my-app/api .
# 2. Tag it with the ECR URI
docker tag my-app/api:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:latest
# 3. Also tag with a version (never rely on latest alone in production)
docker tag my-app/api:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:v1.2.0
# 4. Push both tags
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:v1.2.0
Pulling an Image (on EC2 or ECS)
From EC2 (after authenticating):
docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app/api:v1.2.0
ECS pulls automatically when a task starts — you just reference the ECR URI in your task definition.
IAM Permissions Required
To push (from your machine or CI): the IAM user/role needs ecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:PutImage, and related permissions — or the managed policy AmazonEC2ContainerRegistryPowerUser.
To pull (from EC2 or ECS): the instance/task IAM role needs AmazonEC2ContainerRegistryReadOnly.
Lifecycle Policies
Every image push stores data — old tags accumulate and cost money. Lifecycle policies automatically delete old images:
{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 10 tagged images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 10
},
"action": { "type": "expire" }
},
{
"rulePriority": 2,
"description": "Delete untagged images after 1 day",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 1
},
"action": { "type": "expire" }
}
]
}
Set this in: ECR → Repository → Lifecycle policies → Create rule
Image Scanning
ECR can automatically scan images for known vulnerabilities (CVEs) when they're pushed:
Enable in: Repository settings → Image scanning → Scan on push: enabled
After a push, view scan results in the console or:
aws ecr describe-image-scan-findings \
--repository-name my-app/api \
--image-id imageTag=v1.2.0