GreenOps

© 2026 GreenOps. All rights reserved.

npx greenops-scanBlogPrivacy PolicyTermsRefund PolicyMethodology
GreenOps
ProblemSolutionPricingBlognpx greenops-scan
Sign in / Sign upFree audit
← Back to blog
GreenOps dashboard preview

And Don't Forget ECR: The Container Registry That Never Forgets Either

Every build pushes an image. Almost nothing deletes one.

A CI/CD pipeline wired up to Amazon ECR does exactly what it's told: on every merge, every tag, every deploy, it pushes a new image.

app:v1
app:v2
app:v3
...
app:v387
app:v388

Nobody sets out to keep 388 copies of app. It just happens — one push at a time, for as long as the pipeline has existed, with no step that ever says "and now delete the old ones."

Unlike S3 objects or EBS snapshots, container images are usually not huge individually. But base layers, build caches, and multi-stage artifacts add up, and most teams never look at the bill until a repository is in the hundreds of GB.

AWS already flags this as a cost-optimization check

This isn't a niche GreenOps opinion — AWS's own cost-optimization checks include a dedicated check for ECR repositories without a lifecycle policy. If AWS Support is calling it out, it's common enough to be worth a deliberate look rather than an assumption that "someone probably set that up."

The GreenOps framing for this finding:

ECR repository has no lifecycle policy Old container images may accumulate unnecessarily. Consider retaining only the images required for deployment and rollback.

Why this is easy to miss

  • Nobody "owns" registry cleanup the way they own an EC2 instance or an RDS database — it's infrastructure plumbing, not a service with an owner
  • A repository growing by a few images a day looks identical to one that's fine, until you check the object count
  • Untagged images (left behind by multi-arch builds, failed pushes, or old latest overwrites) don't show up in any dashboard a developer normally looks at
  • CI/CD pipelines are written once and rarely revisited — the step that pushes an image was never paired with a step that prunes old ones

What a lifecycle policy actually does

An ECR lifecycle policy is a set of rules, evaluated in priority order, that expire images matching tag patterns or age thresholds. Nothing runs on a schedule you manage — AWS evaluates and applies the rules automatically whenever the repository changes.

A reasonable starting policy for a typical app repository:

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Keep only the last 10 untagged images",
      "selection": {
        "tagStatus": "untagged",
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "Expire tagged images older than 90 days, except release tags",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["v"],
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 90
      },
      "action": { "type": "expire" }
    }
  ]
}

Apply it with:

aws ecr put-lifecycle-policy \
  --repository-name app \
  --lifecycle-policy-text file://lifecycle-policy.json

Two rules go a long way: clear out untagged orphans aggressively (they're almost never a rollback target), and give tagged release images a generous retention window instead of deleting them outright.

How to audit what you already have

List repositories and check which ones already have a policy:

aws ecr describe-repositories --query 'repositories[*].repositoryName' --output table
aws ecr get-lifecycle-policy --repository-name <your-repo>

An error on the second command means there's no policy — not necessarily a problem, but a signal to check the image count and total size:

aws ecr describe-images \
  --repository-name <your-repo> \
  --query 'length(imageDetails)'

aws ecr describe-repositories \
  --repository-names <your-repo> \
  --query 'repositories[0].repositorySizeInBytes'

A repository with hundreds of images and no lifecycle policy is the pattern worth fixing first.

Where this sits in GreenOps Scan today

GreenOps Scan now ships an ECR module alongside the existing EC2, S3, ECS/EKS, Lambda, RDS/ElastiCache, CloudFront/OpenSearch, VPC endpoint, and EBS snapshot checks. It flags repositories with no lifecycle policy at all, and repositories that have a policy but are still carrying a large pile of untagged images, so you don't have to run the aws ecr commands above by hand.

Tags: #AWS #ECR #ContainerRegistry #CloudCostOptimization #CICD #LifecyclePolicy #CloudWaste #GreenOps

More posts

View all posts →

AWS Told Thousands of Customers They Owed Trillions. Here's the Real Lesson.

July 2026's viral Cost Explorer bug showed some accounts trillion-dollar forecasts due to a unit-pricing error — and some teams panic-deleted production resources over a number that was never real. Why every team needs a second, independently-computed cost number that doesn't share a pipeline with AWS's own dashboard.

The $0.02/GB Charge That Never Shows Up as Its Own Line Item

Multi-AZ RDS replication, cross-zone load balancing, and AZ-spread EKS/ECS workloads all generate cross-AZ data transfer costs — billed at ~$0.01/GB each way and folded invisibly into whichever service caused it. Detected with zero new IAM permissions.

Your AWS Logs Never Expire — and CloudWatch Has Other Meters Running

CloudWatch bills on six meters at once, and its defaults compound forever: log groups with no retention, $0.50/GB ingestion, per-dimension custom metrics, and the GetMetricData polling tax behind third-party monitoring bills.