compute

Amazon ECR

The managed registry where your container images live so ECS, App Runner, Lambda, and EC2 can pull them. Here's when ECR earns its place, what it costs, which role does the pulling, and the registry mistakes Design Beaver catches as you draw.

Updated September 6, 2026

What ECR is

ECR is AWS’s own container registry. You push a built image to a repository, and your compute pulls it by URI when it deploys or starts a task. ECS and Fargate, App Runner, Lambda container functions, and plain Docker on EC2 all pull from it. It stores other OCI artifacts too, like Helm charts.

Past storage, it does the things you’d otherwise bolt on: vulnerability scanning on push, lifecycle policies that expire old images, tag immutability, cross-region and cross-account replication, and a pull-through cache that mirrors upstream registries like Docker Hub into your private registry.

Design Beaver models ECR as a regional, API-accessed registry rather than a subnet-resident resource, and it validates as you draw which services pull from it and what that pull actually needs.

When to use ECR (and when not to)

Reach for ECR when you’re already running containers on AWS and you want the registry next to the compute. IAM controls access, so there’s no second set of registry credentials to rotate. Scan-on-push, lifecycle cleanup, and replication come with it. If Docker Hub rate limits are breaking your builds, a pull-through cache rule solves that without changing how your images are referenced.

Don’t add it to an architecture that isn’t running containers — a registry only earns its place when something pulls images from it. It isn’t an object store for arbitrary files, and it isn’t a CDN. ECR serves the registry protocol, not a cacheable website.

Variants: private and public registries

The real choice is one registry type. Everything else — scan-on-push, tag immutability, lifecycle rules — is a per-repository toggle inside whichever you pick.

OptionWhat it is
ECR PrivatedefaultPer-account, per-region private registry. Access is controlled by IAM (identity policies) and per-repository resource policies. This is what ECS/Fargate, App Runner, and Lambda container functions pull from in a typical private architecture. Supports scan-on-push, lifecycle policies, tag immutability, and cross-region/cross-account replication.
ECR Public (Public Gallery)A public registry (gallery.ecr.aws) for images you want anyone to pull anonymously, similar to Docker Hub public repos. Distinct pricing and data-transfer rules from private. Also usable as an upstream source for a pull-through cache rule.

ECR pricing in plain English

You pay for what you store and what leaves the region. Pulls to same-region compute cost nothing, which makes co-location the single easiest saving.

Pay per GB of images stored per month, plus data-transfer-out charges. Data transfer IN is free; transfer between ECR and other AWS services in the same region (EC2, Lambda, Fargate, App Runner) is free. Cross-region and internet egress are charged.

OptionRepresentative rate
private~$0.10/GB-month stored. New accounts get 500 MB/month free for 12 months.
public50 GB/month always-free storage. Egress to non-AWS destinations free up to 500 GB/month anonymous or 5 TB/month when authenticated with an AWS account; standard AWS data-transfer-out rates apply above those thresholds.

Keeping the bill down

  • Use lifecycle policies to expire old/untagged image versions automatically instead of paying to store every build forever
  • Keep compute in the same region as the repository — same-region pulls to EC2/Lambda/Fargate/App Runner have no data-transfer charge
  • Use pull-through cache instead of re-pulling upstream public images repeatedly across many builds

us-east-1 (rates vary by region). Rates as of 2026-09. Verify at the official pricing page before using for real cost estimates — this list is not kept in sync with AWS pricing changes.

How ECR connects to other services

Every ECR edge is a pull, and every pull is done by the platform — the ECS agent, the App Runner service, the Lambda service, or a Docker client using an instance profile. Your application code is not calling ECR through the SDK.

  • ECS

    ECS (on EC2 or Fargate) pulls the container image from an ECR private repository when it starts a task, using the image URI in the task definition (aws_account_id.dkr.ecr.region.amazonaws.com/repo:tag)

  • App Runner deploys a service from a container image stored in an ECR private repository, pulling the image when the service is created or a new image is deployed

  • A Lambda function packaged as a container image (up to 10 GB) is deployed from an ECR private repository — during function create/update, the Lambda service pulls the image from ECR, optimizes it, and deploys it

  • EC2

    An application or CI job running Docker on an EC2 instance pulls (or pushes) images to an ECR repository with `docker pull` / `docker push` after authenticating to the registry

  • Compute in a private subnet reaches ECR privately through AWS PrivateLink interface VPC endpoints, with no internet gateway or NAT needed for the pull

That distinction decides where the permission goes, and it’s the thing people get wrong:

  • ECS and Fargate pull with the task execution role, not the task role. It needs ecr:GetAuthorizationToken, ecr:BatchGetImage, and ecr:GetDownloadUrlForLayer.
  • App Runner pulls with an access role trusting build.apprunner.amazonaws.com. The managed policy AWSAppRunnerServicePolicyForECRAccess covers it.
  • Lambda container functions are pulled by the Lambda service at deploy time.
  • EC2 running Docker authenticates and pulls using the instance profile.

One detail that breaks hand-written policies: ecr:GetAuthorizationToken is registry-wide and must be granted on Resource: *. The auth token isn’t scoped to a single repository, so a policy that carefully limits it to one repo ARN just fails.

What ECR can’t connect to

A registry looks like it should plug into more things than it does. Design Beaver flags the edges that don’t exist rather than letting them onto the canvas.

  • Route 53 is DNS; ECR is not a DNS target you point a domain at. Image pulls go to the AWS-managed registry endpoint (aws_account_id.dkr.ecr.region.amazonaws.com) using the Docker registry protocol with AWS authentication — you don't front a registry with a custom Route 53 record, so there is no architectural edge between them.

  • RDS

    A relational database and a container image registry have no native integration. Images are not stored in, pulled from, or otherwise exchanged with RDS — an application would use ECR (for its image) and RDS (for its data) independently, never as a direct connection.

  • CloudFront is a CDN for cacheable HTTP content; ECR is not a CloudFront origin. Container image pulls use the OCI/Docker registry protocol with per-request AWS auth (and store layers in S3), not cacheable HTTP objects delivered through an edge network, so ECR is not fronted by CloudFront.

Anti-patterns Design Beaver catches

These three account for most ECR incidents — two break deploys, one quietly inflates the bill.

ECS/Fargate tasks in a private subnet pulling from ECR with no NAT gateway and no VPC endpoints

Why it breaksThe image pull needs to reach the ECR API, the ECR Docker endpoint, and S3 (for layers); with no route to any of them the task fails with a CannotPullContainer error

Do this insteadAdd interface VPC endpoints for ecr.api and ecr.dkr plus an S3 gateway endpoint, or give the subnet egress via a NAT gateway

Putting ECR pull permissions on the ECS task role instead of the task execution role

Why it breaksThe ECS container agent pulls the image using the execution role before your container runs; permissions on the task role (used by your running code) don't help the agent fetch the image

Do this insteadGrant ecr:GetAuthorizationToken / ecr:BatchGetImage / ecr:GetDownloadUrlForLayer on the task execution role

Never expiring old images, so every CI build accumulates in the repository forever

Why it breaksYou pay per-GB storage for images nothing will ever pull again, and untagged layers pile up

Do this insteadAdd a lifecycle policy to expire untagged images and cap how many tagged versions are retained

Gotchas that bite in production

  • Private-subnet pulls need three endpoints. ecr.api, ecr.dkr, and the S3 gateway endpoint, because layers live in S3. Miss the S3 one and the manifest fetch succeeds while the layer download hangs — the confusing failure where connectivity “looks fine.”
  • Execution role, not task role. The ECS agent fetches the image before your container starts. ECR permissions on the task role produce CannotPullContainer every time.
  • GetAuthorizationToken can’t be scoped. Grant it on Resource: *. Scoping it to a repository ARN is a policy that will never authorize a pull.
  • Images accumulate silently. Without a lifecycle policy every CI build stays forever, and you keep paying to store images nothing will pull again.

Further reading

Frequently asked questions

When should you use Amazon ECR?
Use ECR when you build container images and run them on AWS compute, and you want the registry in the same account and region. It gives you IAM-scoped access to images instead of a separate set of registry credentials, plus scan-on-push, lifecycle cleanup, and replication without extra tooling. It's also the fix for Docker Hub pull rate limits — a pull-through cache rule mirrors upstream images into your own registry. Skip it if you aren't running containers, if you need a general-purpose object store (that's S3), or if you want a CDN for web assets (that's CloudFront).
Why does an ECS task fail with CannotPullContainer when pulling from ECR?
Almost always one of two causes. Either the ECR permissions are on the task role instead of the task execution role — the ECS agent pulls the image before your container runs, so permissions on your app's role never reach it — or the task sits in a private subnet with no network path to ECR. Design Beaver flags both as anti-patterns as you draw.
Which VPC endpoints do you need to pull from ECR in a private subnet?
Three, not one. You need interface endpoints for ecr.api (the ECR API) and ecr.dkr (Docker pull and push), plus an S3 gateway endpoint, because ECR stores image layers in S3. Forgetting the S3 endpoint is the classic failure — the manifest fetch succeeds and the layer download hangs, so the pull fails even though ECR connectivity looks fine.
How much does Amazon ECR cost?
You pay per GB of images stored per month, plus data transfer out. Transfer in is free, and so are pulls from compute in the same region — EC2, Lambda, Fargate, or App Runner. Cross-region and internet egress are charged. The cost that creeps up on people is storage — without a lifecycle policy, every CI build stays in the repository forever. Verify current rates on the official pricing page before estimating a real bill.
Can CloudFront serve container images from ECR?
No. CloudFront caches HTTP content; ECR speaks the OCI/Docker registry protocol with per-request AWS authentication and stores layers in S3. There's no CloudFront origin configuration for a registry, so Design Beaver flags an ECR → CloudFront edge as invalid.

Validate your ECR architecture as you draw

Design Beaver checks your AWS design in real time — missing queues, invalid connections, and security anti-patterns, caught before you ship. It’s live in beta, free, and runs in your browser. Sign in with Google or GitHub to save your work.

Sign up for free! →

Prefer email? Get new features in your inbox:

Get new features in your inbox

No spam — product updates only, and you can unsubscribe from any email.

← All supported services