containersnetworkingcost-optimizationkubernetes

Container image pull cost: what your registry traffic is worth

Every pod start pulls an image. Multiply a 1.2 GB image by a few thousand pulls a day and registry traffic becomes a real line item, especially when the registry sits in another region or another cloud.

The C3X Team··7 min read

Quick answer

Pulling an image from ECR to compute in the same AWS region is free, and ECR storage costs 0.10 dollars per GB-month. Pulling across regions costs about 0.02 dollars per GB, and pulling to another cloud or on-premises pays internet egress at 0.09 dollars per GB. A cluster performing 5,000 pulls per day of a 1.2 GB image moves 6 TB daily; in-region that is free, cross-region about 3,600 dollars per month, and to the internet about 16,500 dollars. The controllable levers are image size, layer caching, and registry placement.

Container images feel like a build-time concern, but they are a runtime network cost. Every time a pod is scheduled onto a node that does not already have the image cached, the full set of missing layers crosses a network. In an autoscaling cluster with frequent deployments and spot interruptions, that happens constantly, and the volume is large because images are large.

The rates

PathRate6 TB/day monthly cost
ECR to same regionFree0 USD
ECR to another AWS region0.02 per GBabout 3,600 USD
ECR to internet0.09 per GBabout 16,500 USD
ECR storage0.10 per GB-monthvaries with retention
Pull through NAT gateway0.045 per GB processedabout 8,200 USD

The NAT gateway line is the one that catches people. Nodes in a private subnet pulling from a public registry endpoint route through NAT, paying 0.045 dollars per GB of data processing even when the registry is in the same region. An interface VPC endpoint for ECR, at 0.01 dollars per hour per availability zone plus 0.01 dollars per GB, is substantially cheaper at volume and also avoids the public path. The comparison is laid out in VPC endpoint versus NAT gateway.

Image size is the primary lever

Most production images are far larger than they need to be. A Python service built on a full python:3.12 base is around 1.0 GB; the same application on python:3.12-slim is around 150 MB, and on a distroless or Alpine base with a multi-stage build it can be under 80 MB. A Java service on a full JDK image is often 700 MB where a JRE-only runtime layer plus a jlink-trimmed runtime is under 200 MB.

Going from 1.2 GB to 200 MB is an 83 percent reduction, which on the 6 TB per day example takes cross-region pull cost from about 3,600 dollars to about 600 dollars per month. It also cuts pod startup time, which matters more than the money for autoscaling responsiveness: a node that must pull 1.2 GB before serving traffic adds 30 to 90 seconds to every scale-out event.

Layer caching and why it fails

Docker layers are content-addressed, so a node that already has a layer skips downloading it. In theory, a deployment that changes only application code re-pulls only the final small layer. In practice this breaks in predictable ways: copying the entire source tree before installing dependencies invalidates the dependency layer on every commit; using a mutable tag like latest as a base means the base layer changes unpredictably; and building on a different architecture or base image digest invalidates everything.

Ordering a Dockerfile so that dependency manifests are copied and installed before application source is the single highest-value fix. A well-ordered image where only a 15 MB application layer changes between deploys pulls 15 MB per node instead of 1.2 GB, a 98 percent reduction on redeploys.

Node churn multiplies everything

Pull volume is proportional to how often nodes are new. A cluster with stable nodes pulls an image once per node per deploy. A cluster running heavily on spot instances with frequent interruptions, or one using aggressive scale-down, pulls on every new node. If a 200-node cluster replaces 40 percent of its nodes daily and runs 30 distinct images averaging 400 MB, that is 80 nodes times 30 images times 400 MB, or about 960 GB per day of pulls that stable nodes would not have needed.

Mitigations include pre-baking common images into the node AMI, running a pull-through cache or registry mirror inside the cluster, and using a node image that already contains the base layers your images share. A registry mirror in-cluster turns N pulls from the registry into one pull plus N local transfers, which are free within the zone.

Retention keeps storage in check

ECR storage at 0.10 dollars per GB-month sounds trivial until a CI pipeline pushes an image per commit. At 60 commits per day and 400 MB of unique layers per image, that is 24 GB per day, 720 GB per month, and 8.6 TB per year, costing about 860 dollars per month by the end of the year if nothing is cleaned. A lifecycle policy keeping the last 20 images per repository plus anything tagged for release typically cuts registry storage by 90 percent.

Registry placement

Put the registry where the clusters are. If you run in three regions, replicate the registry to all three rather than pulling cross-region: ECR cross-region replication charges the transfer once per image version rather than once per pull, which for an image pulled 5,000 times is a saving of roughly four orders of magnitude on that image. Price your container platform against the resource catalog before it scales.

FAQ

Does pulling container images cost money?

Within the same AWS region, pulling from ECR is free and you pay only storage at 0.10 dollars per GB-month. Cross-region pulls cost about 0.02 dollars per GB and pulls to another cloud or on-premises pay internet egress at 0.09 dollars per GB. Nodes in private subnets pulling through a NAT gateway pay 0.045 dollars per GB in data processing even for same-region pulls.

How much can smaller images save?

A Python service on a full base image is around 1.0 GB; the same app on a slim base is around 150 MB and on a distroless multi-stage build under 80 MB. Going from 1.2 GB to 200 MB is an 83 percent reduction, taking a 6 TB per day cross-region pull cost from about 3,600 dollars to about 600 dollars monthly, while also cutting 30 to 90 seconds from scale-out.

Why does layer caching stop working?

Common causes are copying the entire source tree before installing dependencies, which invalidates the dependency layer on every commit; using a mutable base tag whose digest changes unpredictably; and building against a different architecture or base digest. Ordering the Dockerfile so dependency manifests are installed before application source is the highest-value fix, often reducing redeploy pulls by 98 percent.

How does node churn affect pull cost?

Directly. A cluster with stable nodes pulls once per node per deploy, while one running heavily on spot with frequent interruptions pulls on every new node. A 200-node cluster replacing 40 percent of nodes daily across 30 images averaging 400 MB moves about 960 GB per day in pulls that stable nodes would not have needed.

How do I cut registry traffic in a large cluster?

Pre-bake common images into the node machine image, run a pull-through cache or registry mirror inside the cluster so N pulls become one external pull plus N free in-zone transfers, use a shared base image across services so layers are reused, and replicate the registry into each region you run in rather than pulling cross-region for every node.

How does C3X help with container platform cost?

C3X prices infrastructure from Terraform before deployment, including registries, VPC endpoints, NAT gateways, and node groups. Because whether image pulls are free, NAT-processed, or cross-region is decided by subnet and endpoint configuration in Terraform, seeing those costs in the pull request is where the expensive path gets caught.

What to do next

Catch expensive network paths before they ship. C3X prices Terraform against a live resource catalog. Start with the quickstart.

Try C3X on your own Terraform

Free and open source. No API key required. One command to install, one command to estimate.