Amazon EKS — ECR & Image Management
Amazon Elastic Container Registry (ECR) is the native container registry for EKS. It integrates seamlessly with IAM, supports image scanning, lifecycle policies, and cross-account access.
Create an ECR Repository
AWS CLI
aws ecr create-repository \
--repository-name my-app \
--image-scanning-configuration scanOnPush=true \
--image-tag-mutability IMMUTABLE \
--encryption-configuration encryptionType=KMS
Terraform
resource "aws_ecr_repository" "my_app" {
name = "my-app"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "KMS"
}
tags = {
Environment = "production"
}
}
Push an Image
# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
# Build and tag
docker build -t my-app:v1.0.0 .
docker tag my-app:v1.0.0 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0
# Push
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0
Image Tag Mutability
| Setting | Behavior | Use Case |
|---|---|---|
| IMMUTABLE | Cannot overwrite a tag once pushed | Production — guarantees a tag always points to the same image |
| MUTABLE | Tags can be overwritten | Development — allows re-pushing latest
|
Production: Always use IMMUTABLE tags. Use Git SHA or semantic versioning (v1.2.3) as tags.
Image Scanning
Basic Scanning (Clair-based)
Scans for OS package vulnerabilities on push.
# Trigger manual scan
aws ecr start-image-scan \
--repository-name my-app \
--image-id imageTag=v1.0.0
# Check results
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=v1.0.0 \
--query 'imageScanFindings.findingSeverityCounts'
Enhanced Scanning (Inspector-based)
Continuous scanning — catches newly disclosed CVEs against existing images.
aws ecr put-registry-scanning-configuration \
--scan-type ENHANCED \
--rules '[{"scanFrequency":"CONTINUOUS_SCAN","repositoryFilters":[{"filter":"*","filterType":"WILDCARD"}]}]'
| Feature | Basic | Enhanced |
|---|---|---|
| Scan trigger | On push only | Continuous |
| CVE database | Clair | Amazon Inspector |
| OS packages | ✅ | ✅ |
| Application dependencies | ❌ | ✅ |
| Automatic rescans | ❌ | ✅ |
| Cost | Free | Per image scanned |
Lifecycle Policies
Remove old images automatically:
aws ecr put-lifecycle-policy \
--repository-name my-app \
--lifecycle-policy-text '{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 10 tagged images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 10
},
"action": { "type": "expire" }
},
{
"rulePriority": 2,
"description": "Remove untagged images after 1 day",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 1
},
"action": { "type": "expire" }
}
]
}'
Cross-Account Image Pull
Allow another AWS account to pull from your ECR:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountPull",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::987654321098:root"
},
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability"
]
}
]
}
aws ecr set-repository-policy \
--repository-name my-app \
--policy-text file://ecr-policy.json
ECR Pull-Through Cache
Cache images from Docker Hub, GitHub Container Registry, or public ECR to avoid rate limits:
# Create pull-through cache rule for Docker Hub
aws ecr create-pull-through-cache-rule \
--ecr-repository-prefix docker-hub \
--upstream-registry-url registry-1.docker.io \
--credential-arn arn:aws:secretsmanager:us-east-1:123456789012:secret:docker-hub-creds
# Pull via cache
docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/library/nginx:latest
EKS Pod Image Pull
EKS worker nodes authenticate to ECR automatically using the node IAM role. No imagePullSecrets needed for same-account, same-region pulls.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0
For cross-account or Fargate, you may need an imagePullSecret:
kubectl create secret docker-registry ecr-secret \
--docker-server=987654321098.dkr.ecr.us-east-1.amazonaws.com \
--docker-username=AWS \
--docker-password=$(aws ecr get-login-password --region us-east-1)
Best Practices
-
Use IMMUTABLE tags — never use
latestin production - Enable scan on push — catch vulnerabilities before deployment
- Set lifecycle policies — avoid paying for thousands of old images
- Use enhanced scanning for production repositories — continuous CVE detection
- Use pull-through cache — avoid Docker Hub rate limits, reduce external dependency
- Tag with Git SHA — direct traceability from image to commit