AWSCost

AWS cost · ECR

ECR storage costs $0.10/GB - and CI builds pile it up

ECR storage is the cost your pipeline creates for you. Every push from CI is a new image made of layers, and ECR keeps all of them at $0.10/GB-month. Nothing expires on its own. So the repository that felt free when you set it up grows a little with every merge, and a year of deploys later you are storing hundreds of gigabytes of old builds that nobody will ever pull again. The charge is small per gigabyte, which is exactly why nobody notices it climb.

ECR bills $0.10/GB-month for stored images, and every CI build adds more layers that never delete themselves. Untagged images from overwritten tags pile up invisibly. A single lifecycle policy - expire untagged images after a few days, cap the tagged ones - clears the backlog and keeps it from returning.

How much does ECR storage cost?

At $0.10/GB-month, the bill tracks how much image history you keep - and image layers add up faster than people expect once a base image and dependencies are baked into every build:

Stored image dataCostPer year
100 GB stored$10/month$120/year
500 GB stored$50/month$600/year
2 TB stored$200/month$2,400/year

Why the bill grows: untagged images

The hidden half of ECR storage is untagged images. Push a new build to a tag that already exists - say the latest tag, or a version tag you reuse - and the previous image does not disappear. It loses its tag and stays in the repository as an untagged image, still stored and still billed. A pipeline that deploys many times a day leaves a long trail of them, none visible unless you go looking.

How to see what each repository is storing

List your repositories, then add up the image sizes in the ones that look heavy. Image sizes come back in bytes:

# every repository in the region
aws ecr describe-repositories --query 'repositories[].repositoryName'

# image sizes (bytes) in one repository, newest first
aws ecr describe-images --repository-name my-service \
  --query 'reverse(sort_by(imageDetails,&imagePushedAt))[].imageSizeInBytes'

# check whether a lifecycle policy already exists
aws ecr get-lifecycle-policy --repository-name my-service

The fix: one lifecycle policy per repository

The lasting fix is a lifecycle policy: expire untagged images after a few days and keep only the last N tagged images per repository. That clears the accumulated layers and stops new ones piling up, without anyone remembering to prune. Applying the policy is a write action, so make it deliberately, once you are happy with the rules:

# WRITE ACTION - applies a policy. Review the JSON first.
aws ecr put-lifecycle-policy --repository-name my-service \
  --lifecycle-policy-text file://ecr-lifecycle.json

Watch the pull path too. Pulling images to a private subnet through a NAT gateway adds data-processing charges on top of storage; a gateway or interface endpoint keeps those pulls off the NAT. And the same accumulate-forever problem hits orphaned AMIs and their snapshots, which are worth pruning in the same pass.

Want to know how much of your bill is image layers, snapshots, and other storage nobody prunes? Connect your account read-only and see what it is wasting, in real dollars.

Connect your AWS

The role can only read - Get, Describe, List, nothing else. Read the exact permissions before you deploy it, and delete the stack whenever you want.

Frequently asked questions

How much does ECR storage cost?+

Amazon ECR charges $0.10/GB-month for stored images, in both private and public registries. There is no free storage tier on a normal account, so every gigabyte of image layers you keep is billed.

Why does my ECR bill keep growing?+

Every CI build pushes a new image, and each image is a set of layers ECR keeps forever unless you tell it not to. Old and untagged images are never removed automatically, so storage climbs with every deploy until you add a lifecycle policy.

What is an untagged image in ECR?+

When you push a new image to a tag that already exists, the old image loses its tag but stays in the repository as an untagged image - still stored, still billed. A busy pipeline leaves hundreds of them behind.

How do I reduce ECR storage costs?+

Add a lifecycle policy that expires untagged images after a few days and caps how many tagged images each repository keeps. That alone clears most of the accumulated layers and stops them coming back.

Does pulling images from ECR cost extra?+

Storage is only part of it. Pulling an image to a private subnet through a NAT gateway adds NAT data-processing charges on top, and cross-region or internet pulls add data transfer. Keeping pulls in-region and using a gateway or interface endpoint avoids the NAT hop.

Are ECR lifecycle policies safe?+

They are as safe as the rules you write. Expiring untagged images older than a few days is low risk, since they are superseded builds. Be more careful with rules that count tagged images - keep enough recent versions to roll back to before letting older ones expire.

Related cost breakdowns

More AWS cost breakdowns