MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr 🔥 tagline

Hey there 👋 I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 — present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 — Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms — earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

AWS ECS vs EKS: Choosing a Container Orchestrator

Compare AWS ECS and EKS: features, pricing, complexity, and when to use each.

AWSECSEKSKubernetesContainers

By MinhVo

Introduction

Choosing between AWS ECS (Elastic Container Service) and EKS (Elastic Kubernetes Service) is one of the most common infrastructure decisions teams face when adopting containers on AWS. Both services run containerized workloads, but they differ fundamentally in complexity, flexibility, cost, and operational overhead. The right choice depends on your team's expertise, your application's requirements, and your organization's infrastructure strategy.

Container orchestration on AWS

ECS is AWS's proprietary container orchestration service. It's tightly integrated with the AWS ecosystem — ALB, CloudWatch, IAM, VPC, and CloudFormation. ECS is simpler to set up and operate because AWS manages the control plane. You define task definitions (container configurations), services (desired count and load balancing), and ECS handles scheduling, health checks, and scaling.

EKS is AWS's managed Kubernetes service. It runs the Kubernetes control plane (API server, etcd, scheduler) and lets you run standard Kubernetes workloads. EKS gives you the full power of the Kubernetes ecosystem — Helm charts, operators, CRDs, and the vast open-source tooling. But this power comes with complexity — Kubernetes has a steep learning curve and significant operational overhead.

This guide provides a deep technical comparison of ECS and EKS across every dimension that matters: architecture, networking, storage, security, cost, operational complexity, and real-world use cases.

Understanding ECS and EKS: Core Concepts

ECS Architecture

ECS consists of several key components:

  • Task Definition — A JSON/YAML document that describes one or more containers (image, CPU, memory, ports, volumes, environment variables). Similar to a Kubernetes Pod spec.
  • Service — Maintains a desired count of tasks, integrates with load balancers, and handles rolling deployments.
  • Cluster — A logical grouping of compute resources (EC2 instances or Fargate).
  • Capacity Provider — The compute backend: EC2 instances (managed or self-managed) or Fargate (serverless).

ECS uses a proprietary scheduler that places tasks on available compute resources. The scheduler considers CPU, memory, port availability, and placement constraints (affinity, anti-affinity, attributes).

EKS Architecture

EKS consists of the standard Kubernetes components:

  • Control Plane — Managed by AWS. Includes the API server, scheduler, controller manager, and etcd. Runs across multiple AZs for high availability.
  • Worker Nodes — EC2 instances (managed node groups, self-managed, or Fargate) that run your pods.
  • Pod — The smallest deployable unit. One or more containers sharing a network namespace and volumes.
  • Service — Kubernetes abstraction for network access to a set of pods. Types: ClusterIP, NodePort, LoadBalancer.
  • Ingress — HTTP routing rules for external access. AWS Load Balancer Controller provisions ALB/NLB.

EKS gives you the full Kubernetes API. You can use any Kubernetes tool, library, or pattern. This includes Helm, Kustomize, operators, CRDs, network policies, and the entire CNCF ecosystem.

Fargate: Serverless Containers

Both ECS and EKS support Fargate — serverless compute for containers. With Fargate, you don't manage EC2 instances. AWS provisions the right amount of compute for your containers and charges per vCPU and memory consumed.

ECS on Fargate is the most mature integration. EKS on Fargate is more limited — it supports Linux/x86 only, has restrictions on pod specifications, and doesn't support DaemonSets or privileged containers.

Container orchestration architecture

Architecture and Design Patterns

The Microservice Pattern

Both ECS and EKS excel at microservice architectures. Each service runs in its own task/pod, with independent scaling, deployment, and resource allocation. ECS is simpler for teams new to containers; EKS is better for teams with Kubernetes expertise.

The Batch Processing Pattern

Run batch jobs (ETL, data processing, ML training) as ECS tasks or Kubernetes Jobs. ECS tasks with Fargate are ideal for ephemeral workloads — run the task, process data, terminate. Kubernetes Jobs provide more control over parallelism and completion criteria.

The Multi-Tenant Pattern

EKS is better for multi-tenant workloads. Kubernetes namespaces provide logical isolation, network policies enforce traffic isolation, and resource quotas prevent noisy neighbors. ECS has less mature multi-tenancy primitives.

The Hybrid Pattern

Run some workloads on ECS (simple services, APIs) and others on EKS (complex workloads requiring Kubernetes features). Use AWS App Mesh or a service mesh for cross-cluster communication.

Step-by-Step Implementation

ECS Task Definition

{
  "family": "web-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "web",
      "image": "123456789.dkr.ecr.us-east-1.amazonaws.com/web-app:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/web-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:ssm:us-east-1:123456789:parameter/prod/db-url"
        }
      ]
    }
  ]
}

ECS Service with Auto Scaling

// AWS CDK
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
 
const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
 
const taskDef = new ecs.FargateTaskDefinition(this, 'TaskDef', {
  memoryLimitMiB: 512,
  cpu: 256,
});
 
taskDef.addContainer('web', {
  image: ecs.ContainerImage.fromEcrRepository(repo, 'latest'),
  portMappings: [{ containerPort: 3000 }],
  logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'web' }),
});
 
const service = new ecs.FargateService(this, 'Service', {
  cluster,
  taskDefinition: taskDef,
  desiredCount: 3,
  assignPublicIp: false,
});
 
const scaling = service.autoScaleTaskCount({ minCapacity: 2, maxCapacity: 10 });
scaling.scaleOnCpuUtilization('CpuScaling', { targetUtilizationPercent: 70 });
scaling.scaleOnMemoryUtilization('MemoryScaling', { targetUtilizationPercent: 80 });

EKS Deployment

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web
          image: 123456789.dkr.ecr.us-east-1.amazonaws.com/web-app:latest
          ports:
            - containerPort: 3000
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi
          env:
            - name: NODE_ENV
              value: "production"
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: url
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
  name: web-app
  namespace: production
spec:
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app
  namespace: production
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app
                port:
                  number: 80

EKS Horizontal Pod Autoscaler

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

Container deployment

Real-World Use Cases

When to Choose ECS

Choose ECS for teams new to containers, simple microservice architectures, and workloads that benefit from tight AWS integration. ECS is ideal when you want minimal operational overhead and don't need Kubernetes-specific features. ECS on Fargate is the simplest way to run containers on AWS — no cluster management, no node provisioning, no capacity planning.

When to Choose EKS

Choose EKS for teams with Kubernetes expertise, complex workloads requiring operators or CRDs, multi-cloud strategies, and organizations that have standardized on Kubernetes. EKS is also the right choice when you need advanced networking (network policies, service mesh), sophisticated scheduling (affinity, tolerations, priorities), or the Kubernetes ecosystem (Helm, ArgoCD, Istio).

Hybrid Approaches

Some organizations run both — ECS for simple services and EKS for complex workloads. This works when different teams have different expertise levels and different workload requirements.

Migration Scenarios

Migrating from ECS to EKS (or vice versa) is non-trivial. If you're starting fresh, choose based on your long-term strategy. If you're already on one and it's working, don't migrate without a compelling reason.

Best Practices for Production

  1. Start with ECS if you're new to containers — ECS has a gentler learning curve and tighter AWS integration. You can always migrate to EKS later if needed.

  2. Use Fargate for variable workloads — Fargate eliminates capacity planning. Use it for bursty workloads, development environments, and batch processing.

  3. Use EC2 for steady-state workloads — If you have consistent, predictable workloads, EC2 with Reserved Instances or Savings Plans is cheaper than Fargate.

  4. Implement health checks — Both ECS and EKS support liveness and readiness probes. Use them to ensure traffic is only routed to healthy containers.

  5. Use secrets management — Never hardcode secrets in task definitions or deployment manifests. Use AWS Secrets Manager, SSM Parameter Store, or Kubernetes Secrets with external-secrets-operator.

  6. Implement auto scaling — Use target tracking scaling policies (CPU, memory, request count) to automatically adjust capacity based on demand.

  7. Monitor everything — Use CloudWatch for ECS, CloudWatch + Prometheus/Grafana for EKS. Set up alarms for CPU, memory, error rates, and deployment failures.

  8. Use infrastructure as code — Define your ECS services or EKS deployments in CDK, Terraform, or CloudFormation. Never make manual changes to production infrastructure.

Common Pitfalls and Solutions

PitfallImpactSolution
Choosing EKS without Kubernetes expertiseOperational burden, slower velocityStart with ECS, migrate to EKS when team is ready
Not using Fargate for bursty workloadsOver-provisioned, wasted costUse Fargate for variable workloads
Hardcoding secretsSecurity vulnerabilityUse Secrets Manager or SSM Parameter Store
Missing health checksTraffic routed to unhealthy containersConfigure liveness and readiness probes
No auto scalingOver-provisioned or under-provisionedImplement target tracking scaling policies
Ignoring resource limitsNoisy neighbor issues, OOM killsSet CPU and memory limits for all containers
Manual infrastructure changesConfiguration drift, no audit trailUse IaC exclusively
Wrong networking modeService discovery issuesUse awsvpc mode for ECS, VPC CNI for EKS

Cost Optimization

ECS on Fargate: Pay per vCPU and memory per second. No EC2 instance management. Good for variable workloads but more expensive for steady-state.

ECS on EC2: Pay for EC2 instances. Use Reserved Instances or Savings Plans for steady-state workloads. Requires capacity planning.

EKS: Pay $0.10/hour for the control plane + EC2/Fargate costs. The control plane cost is fixed regardless of workload size. Use Karpenter for efficient node provisioning.

Cost comparison (approximate monthly for a small workload):

OptionComputeControl PlaneTotal
ECS Fargate~$70$0~$70
ECS EC2 (t3.medium)~$30$0~$30
EKS EC2 (t3.medium)~$30~$73~$103
EKS Fargate~$70~$73~$143

Comparison Table

FeatureECSEKS
Control PlaneAWS-managed, freeAWS-managed, $0.10/hr
Learning CurveLowHigh
AWS IntegrationDeep (native)Good (add-ons)
Multi-cloudAWS onlyAny Kubernetes
Networkingawsvpc (simple)VPC CNI, network policies
Service MeshApp Mesh, Cloud MapIstio, Linkerd, App Mesh
Deployment ToolsCodeDeploy, CDKHelm, ArgoCD, Flux
EcosystemAWS-nativeCNCF ecosystem
Fargate SupportMatureLimited
ScalingService Auto ScalingHPA, VPA, Karpenter
Best ForAWS-native teamsKubernetes teams

Advanced Patterns

Karpenter for EKS

Karpenter is an open-source node provisioner for Kubernetes. It watches for unschedulable pods and launches the right-sized EC2 instances in real time. This is more efficient than the Cluster Autoscaler, which works with node groups.

ECS Anywhere

ECS Anywhere extends ECS to on-premises and edge infrastructure. Register external instances as ECS capacity providers and run tasks on your own hardware while managing them from the AWS console.

EKS Blueprints

EKS Blueprints provide opinionated, production-ready EKS cluster configurations with add-ons (CoreDNS, VPC CNI, metrics-server, ArgoCD, cert-manager). They accelerate EKS adoption by providing battle-tested defaults.

Future Outlook

Both ECS and EKS are actively developed by AWS. ECS is investing in simplicity — easier networking, better Fargate integration, and tighter AWS service integration. EKS is investing in the Kubernetes ecosystem — better Fargate support, Karpenter integration, and multi-cluster management.

The broader trend is convergence — ECS is adopting more Kubernetes-like concepts (service connect, capacity providers), while EKS is becoming easier to operate (EKS Auto Mode, managed node groups). The gap between the two services is narrowing.

Community Resources and Further Learning

The technology landscape evolves rapidly, making continuous learning essential for maintaining expertise. Building a systematic approach to staying current with developments in your technology stack ensures you can leverage new features and avoid deprecated patterns.

Curated Learning Pathways

Rather than consuming content randomly, create structured learning pathways aligned with your current projects and career goals. Start with official documentation and specification documents, which provide the most accurate and comprehensive information. Follow this with hands-on tutorials and workshops that reinforce concepts through practical application.

Technical blogs from framework maintainers and core team members often provide deeper insights into design decisions and upcoming features. Subscribe to the official blogs of your primary frameworks and libraries to stay ahead of breaking changes and deprecation timelines.

Contributing to Open Source

Contributing to open-source projects in your technology stack provides unparalleled learning opportunities. Start with documentation improvements and bug reports, then progress to fixing small issues tagged as "good first issue" in your favorite projects. This direct engagement with maintainers and the codebase accelerates your understanding far beyond what passive learning can achieve.

# Setting up for contribution
git clone https://github.com/project/repository.git
cd repository
git checkout -b fix/issue-description
 
# Run the project's contribution setup
npm run setup:dev
npm run test  # Ensure tests pass before making changes
 
# Make your changes, then run the full test suite
npm run test:full
npm run lint
npm run build
 
# Submit your contribution
git add -A
git commit -m "fix: description of the fix
 
Closes #1234"
git push origin fix/issue-description

Building a Technical Knowledge Base

Maintain a personal knowledge base that captures insights, solutions, and patterns you discover during your work. Tools like Obsidian, Notion, or even a simple Markdown repository can serve as an external memory that grows more valuable over time.

Organize your notes by topic rather than chronologically, and include code examples, links to relevant documentation, and explanations of why certain approaches work better than others. When you encounter a particularly insightful article or conference talk, write a summary that captures the key takeaways and how they apply to your current projects.

Follow key conferences and their published talks to stay informed about emerging patterns and best practices. Many conferences publish recorded talks on YouTube within weeks of the event, making world-class technical content freely accessible.

Join relevant Discord servers, Slack communities, and forums where practitioners discuss real-world challenges and solutions. These communities provide early warning about emerging issues and access to collective wisdom that isn't available through formal documentation.

Mentorship and Knowledge Sharing

Teaching others is one of the most effective ways to deepen your own understanding. Consider writing technical blog posts, giving talks at local meetups, or mentoring junior developers. The process of explaining concepts to others forces you to organize your knowledge and identify gaps in your understanding.

Pair programming sessions with colleagues of different experience levels create mutual learning opportunities. Senior developers gain fresh perspectives on problems they've solved the same way for years, while junior developers benefit from exposure to production-grade thinking and decision-making processes.

Conclusion

ECS and EKS are both excellent container orchestration services. The choice depends on your team's expertise, your application's requirements, and your infrastructure strategy.

Key takeaways:

  1. ECS is simpler — tighter AWS integration, lower learning curve, no control plane cost
  2. EKS is more powerful — full Kubernetes ecosystem, multi-cloud support, advanced scheduling
  3. Use Fargate for variable workloads, EC2 for steady-state workloads
  4. Start with ECS if you're new to containers; choose EKS if you need Kubernetes features
  5. Implement health checks, auto scaling, and secrets management regardless of choice
  6. Use infrastructure as code for all container infrastructure
  7. Monitor costs carefully — EKS control plane adds ~$73/month

Start by evaluating your team's container expertise and your workload requirements. If you need Kubernetes features (operators, CRDs, network policies, multi-cloud), choose EKS. If you want the simplest path to running containers on AWS, choose ECS. Build a proof of concept with your chosen service before committing to production.