An ECR lifecycle policy is the rule that keeps a container registry from storing every image ever built forever; without one, ECR, Azure Container Registry and Artifact Registry grow with every commit, and every untagged image, every image of a branch from two years ago and every intermediate layer is billed per GB and month, on Azure beyond the volume the tier includes. The saving comes from a rule that deletes untagged images after days and images per repository beyond a count, first in a preview and then automatically. The fix belongs to the team that maintains the build pipeline, because the pipeline produces the images and assigns the tags.

Symptom

The bill shows registry storage growing with the number of builds. On AWS it is lineItem/UsageType EUC1-TimedStorage-ByteHrs under AmazonECR, per repository as lineItem/ResourceId, plus DataTransfer-Out-Bytes for pulls into other regions and to the internet. On Azure they are MeterCategory Container Registry with Basic, Standard or Premium registry days and Storage meters for the volume above the included one. On Google Cloud Artifact Registry carries the SKUs Artifact Registry Storage per region and Network Egress per GB. What containers and registries cost in principle is in containers and Kubernetes; this page covers the images nobody pulls any more.

noyesEvery build pushes an image;the tag moves on, theold image staysA rule thatdeletes it?The storage line grows with thebuilds, per GB and month:TimedStorage-ByteHrs, Storageabove the included volume,Artifact Registry StorageThe storage line levels offat the images the rule keepsGrowing storage at steadypulls: the symptom; pullselsewhere are the egressline beside it
Without a rule every build adds to a storage line billed per GB and month while the pulls beside it stay steady

How to detect it

AWS

The repositories with and without a lifecycle policy, and the untagged images per repository:

for r in $(aws ecr describe-repositories --query 'repositories[].repositoryName' --output text); do
  aws ecr get-lifecycle-policy --repository-name "$r" --query 'repositoryName' --output text 2>/dev/null || echo "$r: no policy"
done
aws ecr describe-images --repository-name <repository> --filter tagStatus=UNTAGGED \
  --query 'imageDetails[].[imageDigest,imagePushedAt,imageSizeInBytes,lastRecordedPullTime]' --output table

lastRecordedPullTime says when an image was last pulled, and imageSizeInBytes the size of its layers. Trusted Advisor flags under "Amazon ECR Repository without lifecycle policy configured" every private repository without a policy (read on 2026-10-21). In billing data the candidate is a repository with growing TimedStorage-ByteHrs at steady pulls (AWS Cost and Usage Report).

Azure

The registries with tier and usage, the repositories, and the untagged manifests per repository:

az acr list --query "[].{name:name, rg:resourceGroup, sku:sku.name}" -o table
az acr show-usage --name <registry> -o table
az acr manifest list-metadata --registry <registry> --name <repository> --query "[?tags==null].{digest:digest, size:imageSize, created:createdTime, lastUpdate:lastUpdateTime}" -o table

az acr show-usage shows the stored volume against the volume the tier includes. The acr purge command runs as an ACR task in the registry's container, with --filter on repository and tag, --ago as the age limit, --untagged for manifests without tags and --dry-run for the preview; --untagged-only deletes only manifests without tags (purge tags and manifests). Azure Advisor has no recommendation on registries. In the cost export the candidate is a registry with Storage meters above the tier's volume (Azure cost exports).

Google Cloud

The repositories with size and cleanup policies:

gcloud artifacts repositories list --format="table(name.basename(),location,format,sizeBytes,cleanupPolicies.list())"
gcloud artifacts docker images list <location>-docker.pkg.dev/<project-id>/<repository> --include-tags \
  --filter="-tags:*" --format="table(package,version,createTime,updateTime)"

Cleanup policies in Artifact Registry delete by tagState, olderThan, newerThan and name prefixes, keep with a keep policy or a number of most recent versions, and run first as a dry run whose result lands in the audit logs (cleanup policies). There is no recommender for registries. In the billing export the candidate is a repository with a growing Artifact Registry Storage line (Google Cloud billing export).

How to fix it

  1. Decide the rule: delete untagged images after seven days, keep the last 30 tagged images per repository, exempt release tags by pattern. Whoever pulls by digest instead of by tag must not delete untagged manifests; the pipeline shows which it is.

  2. Set the rule and preview it first. AWS, as JSON with a preview (ECR lifecycle policies):

    {
      "rules": [
        {"rulePriority": 1, "description": "untagged after 7 days",
         "selection": {"tagStatus": "untagged", "countType": "sinceImagePushed", "countUnit": "days", "countNumber": 7},
         "action": {"type": "expire"}},
        {"rulePriority": 2, "description": "keep 30 images",
         "selection": {"tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 30},
         "action": {"type": "expire"}}
      ]
    }
    

    aws ecr start-lifecycle-policy-preview --repository-name <repository> --lifecycle-policy-text file://policy.json shows what the rule would hit; aws ecr put-lifecycle-policy --repository-name <repository> --lifecycle-policy-text file://policy.json sets it, and images expire within 24 hours. Azure: az acr run --registry <registry> --cmd "acr purge --filter '<repository>:.*' --ago 30d --untagged --dry-run" /dev/null, then without --dry-run, as a scheduled task with az acr task create --schedule "0 1 * * Sun"; on Premium registries also the retention policy for untagged manifests. Google Cloud: gcloud artifacts repositories set-cleanup-policies <repository> --location=<location> --policy=policy.json --dry-run, then --no-dry-run.

  3. Check the egress side: pulls from other regions and from the internet pay data transfer; a replica in the cluster's region and pull-through caches for base images lower it (data transfer optimisation).

  4. Correct the template: every new repository comes with a policy from the same module, and the pipeline tags images with unique tags so untagged manifests are safe to delete.

Deleted images leave the storage line in the next daily export, on ECR within 24 hours of expiry.

yesnoThe build pipeline's teamdecides the rule: untaggedimages after days, taggedones beyond a count, releasetags exempt by patternDoes the pipelinepull by digest?Untagged manifests stay;the rule only countstagged imagesUntagged manifests goafter days as wellSet the rule and preview itfirst; then it runsautomaticallyCheck the egress side: areplica in the cluster'sregion, pull-through cachesfor base imagesCorrect the template: everynew repository with a policyfrom the same module; uniquetags from the pipelineDeleted images leave thestorage line in the nextdaily export
The rule is decided with the pipeline, previewed before it runs, and the template gives every new repository the same rule and unique tags

Risks and side effects

  • Deleted images are unrecoverable; a deployment that pulls an old image by digest breaks when the manifest is gone. The preview and the dry run come before every rule.
  • A count per repository, imageCountMoreThan, also deletes an old release image a rollback needs; release tags need a keep rule with higher priority or a tag pattern the delete rule exempts.
  • Multi-architecture images consist of a manifest list and per-architecture manifests that look untagged; a rule on untagged must not hit them, and ECR protects referenced manifests while Azure and Google Cloud protect only by age.
  • A purge with --ago 0d and --untagged without a filter empties a registry; acr purge warns of it itself.
  • A smaller repository lowers the storage line, not the egress line; a cluster that pulls its image hourly from another region keeps costing.

How to verify the saving

Before: per registry or repository, the stored GB-months and their amount in the last full month, TimedStorage-ByteHrs, the Storage meters above the tier, Artifact Registry Storage, and beside them the number of untagged images. After: the same lines in the first full month after the rule; the stored volume has to fall to the rule's level and then grow with the number of active images, not with the number of builds. The estimate is the size of the images the preview hits times the price per GB-month; the release makes it accepted, the rule implemented, and the following month's invoice realises it in the pattern of estimated, accepted, implemented, verified. The number of repositories without a rule is the figure that then stays at zero.

Sources

Read on 2026-10-21. Amazon ECR lifecycle policies, Trusted Advisor cost optimization checks, aws ecr, Azure Container Registry, purge tags and manifests, az acr, Artifact Registry cleanup policies, gcloud artifacts.