---
cuppy_hub: aws
cuppy_path: patterns/ecs-to-ecr.md
cuppy_section: docs
cuppy_doc: 25C4-36Z9
cuppy_doc_uuid: b758161c-7d93-4f98-8237-d61fdf3e2ffe
cuppy_permalink: https://cuppy.io/aws/d/25C4-36Z9
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/patterns/ecs-to-ecr
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 2bcc0fdfad98cc151689d11ced7147d777d3a6f9bb4b126293b91ca37d5dc733
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# ECS Fargate pulling from ECR

**Three things must line up: the execution role may pull, the repository allows it, and the task's subnet can reach ECR's two endpoints *and* S3.** Image layers are served from S3, so a private subnet with only ECR endpoints still fails.

## What crosses the handoff

| Side | Provides / expects |
|---|---|
| **ECS (Fargate)** | Pulls with the **task execution role**, not the task role, through the task's own network interface in your subnet. The image is pinned when the task or deployment starts. |
| **ECR** | An auth token (`ecr:GetAuthorizationToken`, for the whole registry), image manifests (`ecr.api` / `ecr.dkr`), and layers from S3. For pulls from another account, the **repository policy** must allow the pulling role. |

`AmazonECSTaskExecutionRolePolicy` covers the ECR pull actions and log writes for same-account repositories.

## Known-good: private subnets, no internet

1. **Execution role** trusts `ecs-tasks.amazonaws.com` and has `AmazonECSTaskExecutionRolePolicy` (`guides/ecs/run-fargate-task.md`).
2. **Endpoints** in the task's VPC, with private DNS on and security groups allowing TCP 443 from the tasks' security group:

```
aws ec2 create-vpc-endpoint --vpc-id <vpc> --vpc-endpoint-type Interface \
  --service-name com.amazonaws.<region>.ecr.api --subnet-ids <subnets> \
  --security-group-ids <endpoint-sg> --private-dns-enabled
aws ec2 create-vpc-endpoint --vpc-id <vpc> --vpc-endpoint-type Interface \
  --service-name com.amazonaws.<region>.ecr.dkr --subnet-ids <subnets> \
  --security-group-ids <endpoint-sg> --private-dns-enabled
aws ec2 create-vpc-endpoint --vpc-id <vpc> --vpc-endpoint-type Gateway \
  --service-name com.amazonaws.<region>.s3 --route-table-ids <private route tables>
```

   Add `logs`, and `secretsmanager` or `ssm`, if the task uses `awslogs` or injected secrets.
3. **Task** in those subnets with `assignPublicIp=DISABLED`; its security group allows outbound 443.
4. **Image** referenced by an immutable tag or digest.

The S3 gateway endpoint must be associated with the **route tables of the task's subnets**. If an endpoint policy is set, it must allow reading the ECR layer bucket for the Region (UNVERIFIED bucket name pattern, so not stated). Endpoint policies are covered in `patterns/iam-to-vpc-endpoints.md`.

## Known-good alternatives

- Private subnets routed to a NAT gateway: no endpoints needed.
- Public subnets routed to an internet gateway, with `assignPublicIp=ENABLED`.

## Known-bad

| Configuration | Symptom |
|---|---|
| Public subnet, `assignPublicIp=DISABLED` | `CannotPullContainerError … net/http: request canceled while waiting for connection` |
| Private subnet, no NAT, no endpoints | `ResourceInitializationError: unable to pull secrets or registry auth …` or `context canceled` |
| `ecr.api` and `ecr.dkr` endpoints but **no S3 gateway endpoint** | `… failed to copy: httpReadSeeker: failed open: unexpected status code` |
| S3 gateway endpoint not on the subnets' route table | Same, or `panic: runtime error: invalid memory address or nil pointer dereference` |
| Endpoint security group blocks 443 from tasks | Timeouts: `… connection issue between the task and Amazon ECR` |
| No `executionRoleArn`, or a role without the ECR policy | `… Check that the role has the permissions to pull images from the registry` |
| Repository in another account without a repository policy for the role | Same permissions message |
| Tag `latest` re-pushed or deleted after deployment | `pull image manifest has been retried 5 time(s): failed to resolve ref` |
| Image built for arm64 but task `X86_64` (or the reverse) | Container exits 255 (`exec format error` in the logs: UNVERIFIED wording) |

## Which side is wrong?

- `stoppedReason` mentions **permissions/role** → the IAM side: the execution role, or the repository policy for cross-account pulls.
- Mentions **connection, timeout, canceled, httpReadSeeker** → the network side: route, endpoints, security groups, the S3 gateway.
- `failed to resolve ref` → the image reference.
- The task starts and then exits → not a pull problem: `reference/ecs/errors.md`, exit codes.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
