ECR repositories nothing has pulled from in 90 days
What does ZopNight detect here?
RepositoryPullCount is the one pull signal AWS publishes as a metric, and ZopNight requires the series to actually span 90 days of zeros, plus a repository at least 90 days old holding at least 1 image, before recommending deletion of storage that is provably dead in its entirety.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-023 |
| Category | orphan |
| Severity | low |
| Metric | none — pure configuration read |
| Source | ecr_repo_unpulled.go |
Where it applies
Whole-repository death, not cold images
A related rule asks which images inside a live repository have gone cold. This one asks a blunter question: did anything pull from this repository at all. AWS aggregates its pull metric at the repository level by design (no image or tag dimension exists), so the metric can prove a repository untouched but can never name a stale image inside a busy one. When it reads zero across the window, every image in the repository is dead weight together, and the recommendation prices the repository’s entire storage bill as recoverable.
Each gate removes a way to be confidently wrong
The abstentions are the design. A missing metric means the fetch failed (throttle, grant, regional gap) and is never read as zero; the producer emits explicit zeros on successful empty queries precisely so failure and silence stay distinguishable. The series must actually reach back 90 days, because a fortnight of data supports only a fortnight-sized claim. The repository must be at least 90 days old, or silence means “new”, not “dead”. An empty repository abstains: deleting zero bytes saves zero dollars. And an unpriced repository abstains, since a cost recommendation without a cost is noise.
A self-inflicted measurement wound, disclosed
ZopNight’s own image scanning once overwrote per-image last-pull timestamps fleet-wide, which is partly why this rule leans on the pull counter instead: scanning only ever added to it, so a zero reading is genuinely untouched. The residual error runs in the safe direction. A dead repository might be hidden by our own past pulls for a while, but a live one can never be invented.
Run the measurement
aws cloudwatch get-metric-statistics \ --namespace AWS/ECR --metric-name RepositoryPullCount \ --dimensions Name=RepositoryName,Value=my-repo \ --start-time "$(date -u -v-90d +%Y-%m-%dT%H:%M:%SZ)" \ --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --period 86400 --statistics SumDeletion is permanent, so check the slow cycles first
Rollback images, DR golden images, and compliance archives are dormant on purpose, and
quarterly DR drills or annual rebuilds pull on cycles longer than the window. Confirm none
of those apply, then aws ecr delete-repository --force removes every image irreversibly.
Where dormancy is legitimate, a lifecycle policy thinning old tags captures most of the
saving without the finality.