Amazon EKS — ECR & Image Management

2 minute read

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

  1. Use IMMUTABLE tags — never use latest in production
  2. Enable scan on push — catch vulnerabilities before deployment
  3. Set lifecycle policies — avoid paying for thousands of old images
  4. Use enhanced scanning for production repositories — continuous CVE detection
  5. Use pull-through cache — avoid Docker Hub rate limits, reduce external dependency
  6. Tag with Git SHA — direct traceability from image to commit

Updated: