---
title: "13 — Containers on AWS: ECR, ECS, and EKS, Without the Jargon"
uid: ecr-eks-ecs-containers
tags: ["eks", "aws", "kubernetes", "ecr", "ecs", "roadmap:aws", "docker", "containers", "fargate"]
excerpt: "ECR stores the images, ECS or EKS runs them, Fargate optionally removes the servers underneath. The ECS-vs-EKS decision almost always reduces to: do you want Kubernetes, yes or no?"
date: 2026-08-13T03:28:29+0000
source: https://www.aveshina.my.id/en/blog/ecr-eks-ecs-containers
---

"Docker on AWS, somehow" was the whole container story in my head, which made the acronym wall — ECR, ECS, EKS, Fargate — impossible to navigate. The three-service shape that held: **the AWS container story is one pipeline — ECR stores the images, ECS or EKS runs them, and Fargate optionally removes the servers underneath — and the ECS-versus-EKS decision is almost always reducible to "do I want Kubernetes, yes or no"** [1][2][6]. Once those three pieces were separate in my head, the constellation of acronyms stopped being a wall.

## ECR: the image registry

**ECR — Elastic Container Registry** — is a managed Docker registry [1]. I push images to it from CI; my runtimes pull from it at deploy. Two things that distinguish it from running my own registry:

- **IAM integration.** Access is just IAM policy — my ECS tasks assume a role whose policy allows ecr:GetDownloadUrlForLayer, and pull works; everything else is denied. No registry credentials to manage or rotate.
- **Managed and scaled.** I'm not running the registry's storage or its API. Images live in S3 under the hood, replicated per region, and I pay per GB stored plus data transfer out.

The workflow is plain Docker with a login step:

```
aws ecr get-login-password | docker login --username AWS --password-stdin <acct>.dkr.ecr.<region>.amazonaws.com
docker tag myapp:latest <acct>.dkr.ecr.<region>.amazonaws.com/myapp:latest
docker push <acct>.dkr.ecr.<region>.amazonaws.com/myapp:latest
```

ECR is the boring, load-bearing foundation — nothing else in the stack runs without images in it.

## ECS: AWS's own orchestrator

**ECS — Elastic Container Service** — is AWS's purpose-built container orchestrator [2]. It has its own vocabulary, but the model is small:

- **Task definition** — a JSON recipe describing one or more containers: image, CPU/memory, ports, environment, IAM role. The unit of deployment.
- **Task** — a running instance of a task definition [7]. Like an object is an instance of a class.
- **Service** — a managed controller that keeps N copies of a task definition running, with a load balancer in front and self-healing on failure [3]. The ECS analog of an EC2 Auto Scaling Group.
- **Cluster** — a logical pool of capacity the tasks run on [5].

ECS's appeal is that it's AWS-native, simpler to operate than full Kubernetes, and integrates cleanly with IAM, load balancers, and CloudWatch. The learning curve is gentle, and for a team already inside the AWS ecosystem it's usually the lower-friction choice.

## EKS: managed Kubernetes

**EKS — Elastic Kubernetes Service** — is a managed Kubernetes control plane [6]. AWS runs the API server, etcd, and the scheduler; I run the worker nodes (or let Fargate handle them). I get the full Kubernetes API — Deployments, Services, Ingress, Helm, the whole ecosystem — and portability: a manifest that runs on EKS runs on any other Kubernetes cluster too.

The cost of that power and portability is operational complexity. Kubernetes has a steeper learning curve than ECS, more moving parts, and a richer failure surface. EKS is the right call when the organization has already standardized on Kubernetes, when the workload needs features Kubernetes uniquely offers, or when avoiding platform lock-in is a real requirement. For a small team running a few services, ECS is usually simpler and cheaper to operate.

## Fargate: removing the servers

Both ECS and EKS can run on **EC2 instances I manage** (I pick the instance type, patch the OS, scale the cluster) or on **Fargate**, AWS's serverless container runtime [4]. With Fargate:

- I specify the CPU and memory each task needs; AWS runs it on capacity I never see.
- I pay per second of task runtime, with no idle EC2 to right-size.
- I stop thinking about node patching, bin-packing (juggling which tasks share which node), and cluster scaling.

The trade-off is price (Fargate is more expensive per unit of compute than a well-packed EC2 fleet) and some limits (not every workload fits Fargate's profile). The rule of thumb: **Fargate for ease and small/medium workloads, EC2-backed for cost-optimized scale or special workload requirements.**

```figure
<svg viewBox="0 0 700 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="One image pipeline, two orchestrators. Left: a Docker image is pushed to ECR. The image is pulled by an ECS cluster and an EKS cluster. Under each cluster, a toggle: Fargate (serverless, no nodes) vs EC2 (self-managed worker nodes).">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <defs><marker id="cra" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#64748b"/></marker></defs>

    <!-- ECR -->
    <rect x="20" y="110" width="130" height="60" rx="8" fill="#e0e7ff" stroke="#6366f1"/>
    <text x="85" y="135" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">ECR</text>
    <text x="85" y="152" font-size="9" fill="#1e1b4b" text-anchor="middle">image registry</text>

    <!-- ECS -->
    <rect x="230" y="40" width="200" height="100" rx="8" fill="#dcfce7" stroke="#16a34a"/>
    <text x="330" y="62" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">ECS</text>
    <text x="330" y="78" font-size="9" fill="#052e16" text-anchor="middle">AWS-native orchestrator</text>
    <rect x="245" y="92" width="80" height="36" rx="5" fill="#fef9c3" stroke="#ca8a04"/><text x="285" y="115" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">Fargate</text>
    <rect x="335" y="92" width="80" height="36" rx="5" fill="#fce7f3" stroke="#db2777"/><text x="375" y="115" font-size="9" font-weight="700" fill="#500724" text-anchor="middle">EC2 nodes</text>

    <!-- EKS -->
    <rect x="230" y="170" width="200" height="100" rx="8" fill="#dbeafe" stroke="#2563eb"/>
    <text x="330" y="192" font-size="12" font-weight="700" fill="#1e3a8a" text-anchor="middle">EKS</text>
    <text x="330" y="208" font-size="9" fill="#1e3a8a" text-anchor="middle">managed Kubernetes</text>
    <rect x="245" y="222" width="80" height="36" rx="5" fill="#fef9c3" stroke="#ca8a04"/><text x="285" y="245" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">Fargate</text>
    <rect x="335" y="222" width="80" height="36" rx="5" fill="#fce7f3" stroke="#db2777"/><text x="375" y="245" font-size="9" font-weight="700" fill="#500724" text-anchor="middle">EC2 nodes</text>

    <!-- arrows from ECR -->
    <path d="M150,130 L230,80" stroke="#64748b" stroke-width="1.3" marker-end="url(#cra)"/>
    <path d="M150,150 L230,210" stroke="#64748b" stroke-width="1.3" marker-end="url(#cra)"/>

    <!-- right side: run units -->
    <rect x="500" y="50" width="180" height="40" rx="6" fill="#fff" stroke="#16a34a"/><text x="590" y="74" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">Service → N tasks</text>
    <rect x="500" y="190" width="180" height="40" rx="6" fill="#fff" stroke="#2563eb"/><text x="590" y="214" font-size="10" font-weight="700" fill="#1e3a8a" text-anchor="middle">Deployment → N pods</text>
    <path d="M430,90 L500,70" stroke="#64748b" stroke-width="1.3" marker-end="url(#cra)"/>
    <path d="M430,210 L500,210" stroke="#64748b" stroke-width="1.3" marker-end="url(#cra)"/>
  </g>
</svg>
```

The vocabularies differ ("task" vs "pod", "service" vs "deployment") but the run-time shape is the same: a managed controller keeps N replicas of my container running behind a load balancer, and Fargate vs EC2 is an orthogonal capacity decision.

## How I use this

The container stack is my default the moment an app is stateless, horizontally scalable, and built around the twelve-factor model (a checklist for writing apps that run the same everywhere and scale across many machines). My decision tree: push images to ECR from CI (always — ECR is the entry point); pick ECS unless the organization is already committed to Kubernetes or needs its portability; default to Fargate for small and medium workloads so I'm not patching nodes, and switch to EC2-backed only when scale makes the per-unit cost worth optimizing or a workload needs something Fargate can't provide. ECS-with-Fargate is, for most internal services, the sweet spot — a task definition, a service, a load balancer, and I'm done, with no servers in sight. EKS is a deliberate choice, not a default, because Kubernetes is an operating system in its own right and adopting it commits a team to its complexity.

## References

[1] Amazon Web Services, "What is Amazon ECR?," ECR User Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonECR/latest/userguide/concept-and-components.html](https://docs.aws.amazon.com/AmazonECR/latest/userguide/concept-and-components.html)

[2] Amazon Web Services, "What is Amazon Elastic Container Service?," ECS Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html)

[3] Amazon Web Services, "Amazon ECS services," ECS Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html)

[4] Amazon Web Services, "AWS Fargate," 2024. [Online]. Available: [https://aws.amazon.com/fargate/](https://aws.amazon.com/fargate/)

[5] Amazon Web Services, "Amazon ECS clusters," ECS Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html)

[6] Amazon Web Services, "What is Amazon EKS?," 2024. [Online]. Available: [https://aws.amazon.com/eks/](https://aws.amazon.com/eks/)

[7] Amazon Web Services, "Amazon ECS task definitions," ECS Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html)

```quiz
Q: What is the role of ECR in the AWS container pipeline?
- It is the managed image registry that CI pushes to and the orchestrators pull from
- It is the container runtime that executes tasks
correct: 0
explain: ECR stores images (backed by S3 under the hood) and gates access through IAM. ECS and EKS pull images from it at deploy time.

Q: ECS and EKS differ most fundamentally in that…
- ECS is AWS-native and simpler; EKS is managed Kubernetes with the full K8s API and ecosystem
- ECS only runs on Fargate and EKS only runs on EC2
correct: 0
explain: ECS is AWS's purpose-built orchestrator. EKS gives you Kubernetes — portable, powerful, more complex. Both can run on either Fargate or EC2.

Q: What does AWS Fargate remove from the container story?
- The need to manage the underlying EC2 worker nodes (patching, scaling, bin-packing)
- The need to write a task definition
correct: 0
explain: With Fargate you specify per-task CPU/memory and AWS runs it on capacity you never see. You still define the task; you just stop owning the servers.

Q: An ECS Service is the analog of…
- an EC2 Auto Scaling Group — it keeps N replicas running behind a load balancer with self-healing
- a Dockerfile
correct: 0
explain: A Service maintains a target count of task copies, restarts failed ones, and binds a load balancer in front. It's the controller that makes the fleet elastic.

Q: When is EKS the right choice over ECS?
- When the org has standardized on Kubernetes, needs its portability, or requires K8s-specific features
- When you want the simplest possible orchestration
correct: 0
explain: EKS commits the team to Kubernetes's complexity. ECS is the lower-friction default; EKS is a deliberate choice for portability, ecosystem, or org-wide standardization.
```
