Skip to content
Running the ARGLabs apps on EKS instead of ECS: effort, cost, monitoring and verdict

Running the ARGLabs apps on EKS instead of ECS: effort, cost, monitoring and verdict

Written by Claude Code on 2026-10-14, from the ARGLabs repositories. Prices are us-east-1 on-demand list prices at 730 hours a month, and the baseline is dev, two regions, the full stack. Estimates, not invoices. The 2024 post ECS or EKS chose ECS from the requirements; this one puts numbers on that choice. Both start from Cloud as an Operating System.

What would change for arglabs-workload, statuspage and dependencytrack if they left ECS Fargate for an EKS cluster, what that cluster’s repository would look like, how much it would cost on EC2, Fargate and Auto Mode, and what happens to the sidecars and to the monitoring arglabs-tfmod-ecs-app delivers today.

1. Executive summary

Migrating everything to EKS is not worth it. On the part of the cost that depends on the orchestrator, dev in two regions costs about USD 32 a month today on Fargate Spot, and would go to USD 204 to 275 on EKS with Graviton nodes, or USD 377 on EKS Fargate. That is two control planes per environment (USD 146) before a single pod. Reaching parity with what arglabs-tfmod-ecs-app does today takes between 23 and 36 working days, and two pillars of the monitoring change nature: the task metrics and the per-app cost widget.

If the goal is to learn and write about EKS, the right path is a parallel, parkable track (enable_eks_cluster in dev only, one region, a Graviton managed node group on Spot or Auto Mode), keeping ECS as the platform default. The detail that surprises most: EKS Fargate does not run arm64, and every ARGLabs image is arm64, built on an ARM runner.

Monthly cost, dev, 2 regions
32 → 204
USD a month, the part that depends on the orchestrator: ECS Spot today against the cheapest EKS (Graviton managed node groups on Spot), 6.4 times.
Migration effort
23 to 36
working days for functional parity of the three apps, BigBang and the pipeline.
Monitoring to rebuild
9 of 24
dashboard widgets change their source; the 15 of the ALB stay the same. Autoscaling, logs and the cost widget change mechanism.
EKS Fargate
x86 only
no Graviton, no Spot, private subnets only, plus 256 MB per pod. Ruled out for ARGLabs.

2. What exists today

Three applications run as ECS Fargate services in the aio account, each in its own repository, attached to the ALBs BigBang created. The ECS cluster is an empty, free namespace, created by arglabs-tfmod-bigbang-ecs-cluster (130 lines of Terraform) with Container Insights enhanced in dev, stg and prd.

AppSizeRegionsALBSidecarsSpotParticulars
arglabs-workload0.25 vCPU / 512 MB, arm64us-east-1 (us-east-2 in the baseline)ext, public subnets, Route53 health checkFireLens (own image with S3 output) and ADOT (X-Ray)dev/stg 100%; prd 2 on-demand, then 50/50The example app; 334 MB left for the app after the sidecars
arglabs-cloudteam-statuspage0.25 vCPU / 512 MB, arm64us-east-1intnonesameGatus, a FROM scratch image, no shell
arglabs-cloudteam-dependencytrack2 vCPU / 4 GB, arm64us-east-1intnonesameexternal_container_definition: frontend, apiserver and a wait-for-db init container with dependsOn HEALTHY and SUCCESS; Aurora

What arglabs-tfmod-ecs-app delivers per app and region

3,427 lines of Terraform (2,200 without variables.tf), 133 variables, 798 lines of tests. Everything arrives with the application; nothing is installed in the cluster:

  • Service and task definition on Fargate, capacity providers FARGATE and FARGATE_SPOT per service (min_ondemand, spot_weight), a circuit breaker with rollback, the native ROLLING, BLUE_GREEN, CANARY and LINEAR strategies, ECS Exec.
  • A target group and two listener rules on the shared ALB (host and /<app>/*), a regional Route53 record and, in the repository, the weighted regionless name with a health check.
  • Autoscaling on CPU and memory (target tracking at 70%), emergency step scaling, and request count per target.
  • 8 alarms (high CPU, high memory, emergency, and 5 for the ALB, off with threshold 0), notifying the account’s alerts SNS topic.
  • A 24-widget dashboard: 15 for the ALB, 2 from AWS/ECS, 3 from ECS/ContainerInsights, 3 Logs Insights, and the Cost Explorer cost widget filtered by app_name and env.
  • Sidecars: FireLens (Fluent Bit, its configuration baked into the aio-<app>-fluentbit image, output to stdout plus S3 in the account’s log bucket, partitioned for Athena) and ADOT (OTLP on localhost, X-Ray).
  • IAM: the execution role (ECR, logs, Secrets Manager) and the task role (X-Ray, Exec, EMF), plus the repository’s workload role, trusting ecs-tasks.amazonaws.com.

The pipeline has nothing ECS-specific: the deploy is a terraform apply (wait_for_steady_state = false), and the images are arm64, built on the self-hosted linux.arm64 runner and pushed only to the dev account’s ECR. That is good news for a migration: the build, lotr and the scripts stay the same.

The network is dual-stack, and the apps subnets have no NAT (outbound only over IPv6, through the egress-only gateway, plus the S3 endpoint), which is why the tasks run in public with a public IPv4. The account’s deployer has no eks:*, no iam:*OpenIDConnectProvider* and no xray:*, and is denied creating NAT gateways and VPC endpoints.

3. What it would look like on EKS, keeping the ARGLabs model

The ARGLabs rule is that BigBang delivers the platform and the team brings everything that matters along with the app. On EKS that means one cluster per region and environment, created by BigBang, and an app module that creates the Kubernetes objects through Terraform’s kubernetes provider, with no Helm by hand and no kubectl apply in the pipeline.

ECS today

App repository · tfmod-ecs-app.tfService, task definition, sidecars, TG and rules on the ALB, Route53, autoscaling, alarms, dashboard, cost widget, execution and task roles, secrets injected by ECS.Everything arrives with the app; nothing is installed in the cluster.
BigBang · arglabs-tfmod-bigbangAn empty, free ECS cluster, the int and ext ALBs, the DNS zone, the alerts topic, the log bucket with Athena, CloudTrail, the cost dashboard, the deployer.
AWSFargate, ELB, CloudWatch, X-Ray, Secrets Manager

EKS proposed

App repository · tfmod-eks-app.tfNamespace, Deployment, Service, HPA, PDB, a ServiceAccount with Pod Identity, ExternalSecret, TargetGroupBinding; TG and rules on the ALB, Route53, alarms, dashboard, cost widget.The kubernetes and helm providers read the configuration state.
BigBang · tfmod-eks-cluster.tf (new)The EKS cluster (USD 73 a month), access entries, control plane logs; add-ons: vpc-cni, coredns, kube-proxy, the pod identity agent, metrics-server, CloudWatch observability (Container Insights and Fluent Bit), the AWS LB Controller, External Secrets, ADOT; Graviton node groups, or Fargate profiles, or Auto Mode.Plus what already exists: ALBs, DNS, SNS, the log bucket, CloudTrail.
AWSEC2 or Fargate, ELB, CloudWatch, X-Ray, Secrets Manager
Where each thing lives. The app layer stays in the app repository; the platform layer stops being an empty namespace. On EKS, the BigBang layer gains a control plane with a version calendar, and a list of add-ons with a compatibility matrix.

The decisions that keep the model

  • The shared ALB stays. The target group and the listener rules remain in the app’s Terraform (code reused from ecs-app), and a TargetGroupBinding of the AWS Load Balancer Controller registers the pods in that TG, in ip mode. The controller neither creates nor touches the ALB. The 15 widgets and the 5 alarms of the ALB stay identical.
  • One identity per app. The repository’s workload role gains the pods.eks.amazonaws.com trust and an aws_eks_pod_identity_association with the app’s ServiceAccount. No IRSA, and no OIDC provider per cluster.
  • Secrets. The SECRET_* stay in aio/<app>/<env>/app; an ExternalSecret (External Secrets Operator) materializes them as a Kubernetes Secret, and the Deployment injects them as variables, keeping APP_SECRET_KEYS. The reader of Secrets Manager becomes the operator’s identity, not the execution role.
  • Logs. Fluent Bit becomes an account DaemonSet (part of the amazon-cloudwatch-observability add-on, or the aws-for-fluent-bit chart), with one configuration per cluster doing what s3-output.conf does today: JSON to CloudWatch, and gzip to logs/<app>/YYYY/MM/DD/ in the account’s bucket, with the namespace as the app’s name.
  • Traces. ADOT through the operator’s sidecar injection (an annotation on the pod), or one collector per node. The app only swaps AwsEcsResourceDetector for AwsEksResourceDetector in tracing.py.
  • Ephemeral environments (dev-*) become namespaces in the dev cluster instead of whole stacks: cheaper and faster than today. It is the only point where EKS simplifies something ECS does with effort.

4. The repositories: cluster, BigBang and app module

arglabs-tfmod-bigbang-eks-cluster (new, used only by BigBang)

One cluster per call, in one region, named <scope>-eks-<region short>-<env> (aio-eks-use1-dev), following the pattern of arglabs-tfmod-bigbang-ecs-cluster, but with one file per resource group:

terraform/
  terraform.tf              aws >= 6.x, kubernetes, helm, tls
  variables.tf              name, region, kubernetes_version, compute_mode
                            (mng | fargate | auto), node_*, fargate_*,
                            subnet_ids, enable_* per add-on, log retention
  eks-cluster.tf            aws_eks_cluster: authentication_mode API,
                            public endpoint with an allowlist (or private),
                            ip_family ipv4, control plane logs, upgrade_policy
                            STANDARD (avoids extended support by mistake)
  eks-access.tf             access entries: deployer (cluster admin),
                            AWSReservedSSO_AdministratorAccess_* (admin),
                            PowerUserAccess (view), the pipeline runner
  eks-addons.tf             vpc-cni, coredns, kube-proxy, eks-pod-identity-agent,
                            metrics-server, amazon-cloudwatch-observability
  eks-nodegroup.tf          compute_mode = mng: Graviton managed node group,
                            launch template, Spot or on-demand, 1 to N nodes per AZ
  eks-fargate-profile.tf    compute_mode = fargate: one profile per namespace,
                            built-in logging (ConfigMap aws-logging)
  eks-automode.tf           compute_mode = auto: compute_config, a custom
                            arm64 and Spot NodePool
  helm-lb-controller.tf     AWS Load Balancer Controller (Pod Identity),
                            not needed with Auto Mode
  helm-external-secrets.tf  External Secrets Operator + ClusterSecretStore
  helm-adot.tf              ADOT operator and collector (X-Ray)
  iam.tf                    cluster role, node role, add-on roles
                            (Pod Identity associations)
  log_group.tf              /aws/eks/<name>/cluster, retention per env
  cloudwatch_alarms.tf      nodes NotReady, pods Pending, API server 5xx
  cloudwatch_dashboard.tf   the cluster's Container Insights + cost widget
                            (arglabs-tfmod-cost-widget, service Amazon EKS)
  outputs.tf                cluster_name, endpoint, certificate_authority,
                            oidc_issuer, version, node_security_group_id

Estimate: 1,800 to 2,500 lines of Terraform, plus tests. A helm_release and the kubernetes provider in the same root that creates the cluster is the known weak point of this kind of module (the provider needs the endpoint before it exists). BigBang already splits account creation from configuration, and the same cut solves it here: the cluster in one apply, the Helm add-ons in the next, or everything as aws_eks_addon wherever a managed add-on exists.

Changes to arglabs-tfmod-bigbang and its neighbors

WhereWhat changes
arglabs-tfmod-bigbangA new tfmod-eks-cluster.tf with enable_eks_cluster (default false) and eks_compute_mode; new platform fields per region: eks_cluster_name, eks_cluster_endpoint, eks_cluster_ca, eks_oidc_issuer, eks_version; the Athena table logs gains the columns kubernetes_namespace, kubernetes_pod_name, kubernetes_container_name and kubernetes_pod_id, and the lines-per-task query gains a per-pod sibling.
arglabs-tfmod-bigbang-iamThe deployer gains eks:* (cluster, node groups, add-ons, access entries and Pod Identity associations) and, only if IRSA is used, iam:CreateOpenIDConnectProvider and its relatives. ec2:*, iam:PassRole and iam:CreateServiceLinkedRole already exist. It still cannot create NAT gateways.
arglabs-bigbang-aioenable_eks_cluster = { dev = true, stg = false, prd = false } for the parallel track; the BigBang apply takes 12 to 18 minutes longer while the cluster is on (an estimate: creating an EKS cluster takes 10 to 15 minutes).
arglabs-cloudteam-sharedpipelinesA new eks-app flow (a copy of ecs-app) with an optional kubectl rollout status step after the apply, and an image with kubectl. The self-hosted runner must reach the EKS API endpoint (public, with the runner’s IP on the allowlist, or private, with the runner in the VPC).
arglabs-lotrNothing: the order bigbang, dependencytrack, workload, statuspage stays.

arglabs-tfmod-eks-app: the sibling of ecs-app

Reused from arglabs-tfmod-ecs-app (about 60%)New
alb_tg.tf, alb_rule.tf, route53.tf, sg.tf, the 5 alarms and 15 widgets of the ALB, cost_widget.tf, log_group.tf, the per-env variable structure, the TG and rule testskubernetes_namespace, kubernetes_service_account + aws_eks_pod_identity_association, kubernetes_deployment_v1 (the app, an optional ADOT sidecar, init containers, probes, requests and limits, terminationGracePeriodSeconds), kubernetes_service_v1, kubernetes_horizontal_pod_autoscaler_v2, kubernetes_pod_disruption_budget_v1, kubernetes_manifest for TargetGroupBinding and ExternalSecret, a rewritten dashboard (Container Insights per pod), pod alarms (restarts, pending, OOMKilled), optional KEDA for the request count

In the app repository, tfmod-eks-app.tf would be one more parkable file, with the same invariant as the others: it references no other tf*.tf, it reads the cluster from platform, and iam-role-workload.tf gains the second service principal. The SECRET_*, the ENV_* and the image contract do not change.

5. EC2 (managed node groups), Fargate or Auto Mode

CriterionManaged node groups (Graviton)EKS FargateEKS Auto Mode
arm64 (the current images)yes t4g, m7g, c7gno x86 only, per the official EKS tableyes a custom arm64 NodePool
Spotyes per node groupnoyes per NodePool; the 12% fee is not discounted
Minimum sizeA t4g.small (2 vCPU, 2 GB) already runs; t4g.medium for the small apps, t4g.large for Dependency-Track0.25 vCPU / 0.5 GB per pod, plus 256 MB of overhead per pod, rounded upMore than 1 vCPU and a size of medium or larger: t4g.medium is the smallest (USD 27.47 with the fee)
DaemonSets (Fluent Bit, CloudWatch agent, ADOT)yesno sidecars only; a built-in log router, to CloudWatch, Firehose, Kinesis or OpenSearch onlyyes on immutable Bottlerocket nodes (no writing to /etc)
Subnets and NAT on the ARGLabs networkNodes in the public subnets with a public IPv4, like the tasks today; or the apps subnets with IPv6 egress (to be validated for ECR and STS)private subnets only and the apps subnets have no NAT. Needs NAT (USD 33 per gateway a month) or validated IPv6-only egress to ECR, STS and CloudWatchLike managed node groups
Container Insightsenhanced, per observation (about USD 3 to 10 for a small cluster)enhanced not supported ADOT scraping the kubelet, custom metrics at USD 0.30enhanced
Add-ons includedvpc-cni, coredns, kube-proxy, pod identity; the rest is an add-on or HelmThe same, plus the log routerLB Controller, EBS CSI, CNI, Karpenter and Pod Identity, managed by AWS
Node and AMI upgradesYou trigger them (a node group rolling update)AWS patches, with evictionsAWS, with a 21-day maximum node lifetime
Node accessSSMThere is no nodeNo SSH, no SSM
Cost beyond EC2The node’s EBSPer pod, no Spot, with overhead12% on top of the instance’s on-demand price
Verdict for ARGLabsoption 1 if EKSruled outoption 2 less operations, a bit more expensive

The point that rules Fargate out is not only the price. Today the whole platform is arm64, from the linux.arm64 runner to cpu_architecture = "ARM64", and CLAUDE.md already records the missing x86 build as a gap. EKS Fargate would force x86 images (QEMU emulation on the ARM runner, or a second runner), only to pay more and lose Spot.

6. Costs

Comparing totals does not help: Aurora, Valkey, SQS, SNS, the buckets, Lambda, CloudFront, the ALBs, CloudTrail and DNS cost the same under both models. The tables below isolate the part that depends on the orchestrator: the control plane, the apps’ compute, the system pods and the cluster’s observability. The rest of the full stack, by the estimate in Arglabs, by Claude, is between USD 180 and 220 a month in dev with everything on, and goes away with lotr_destroy_all.

Assumptions and prices (us-east-1, 730 hours a month)

ItemPrice (USD)Source and note
Fargate arm64 (ECS)0.03238 / vCPU-h; 0.00356 / GB-hAWS Fargate pricing. Spot is “up to 70% off”: the model uses 65%
Fargate x86 (EKS Fargate, the only option)0.04048 / vCPU-h; 0.004445 / GB-hAWS Fargate pricing. Plus 256 MB per pod, rounded up to the next combination
EKS cluster73.00 / month (0.10 / h)AWS EKS pricing. Extended support: 0.60 / h, 438.00 / month
t4g.medium (2 vCPU, 4 GB) and t4g.large (2 vCPU, 8 GB)24.53 and 49.06 / month0.0336 and 0.0672 / h. Spot modeled at 60% off. EBS gp3 20 GB: 1.60 per node
Auto Mode fee12% of on-demandThe examples on the EKS pricing page; not discounted by Spot or Savings Plans
NAT gateway32.85 / month per gateway0.045 / h, plus 0.045 / GB. Only in the Fargate scenario (private subnets required)
Container InsightsECS 2.00; EKS 5.00; EKS Fargate 15.00 per clusterEstimates: ECS enhanced at 0.07 / metric (the blog: 2 to 5 for 2 clusters); EKS enhanced at 0.21 per million observations; Fargate without enhanced, custom metrics at 0.30
Control plane logs2.00 per clusterAn estimate, at 0.50 / GB ingested, with api and audit on
System pods on EKS Fargate9.01 per podcoredns ×2, LB controller, metrics-server, ADOT: each lands on the 0.25 vCPU / 0.5 GB x86 minimum

Left out of the comparison because they are the same in both: X-Ray (USD 5 per million traces), alarms (0.10), dashboards (3.00 beyond the three free ones), the cost widget (0.01 per Cost Explorer call), the ALBs, DNS. Network data and transfer were not modeled.

What each app costs a month (USD)

AppECS arm64ECS on-demandECS SpotEKS Fargate x86 (pod rounded up)EKS Fargate on-demand
workload (per region)0.25 vCPU / 0.5 GB7.212.520.25 vCPU / 1 GB10.63
statuspage0.25 vCPU / 0.5 GB7.212.520.25 vCPU / 1 GB10.63
dependencytrack2 vCPU / 4 GB57.6720.182 vCPU / 5 GB75.33

In the scenarios with nodes, us-east-1 takes a t4g.large and a t4g.medium, in two AZs, and us-east-2 a t4g.medium (only the workload, like today’s single task). Dependency-Track asks for 2 vCPU and 4 GB, which no t4g.medium holds (4 GiB in total, about 3.4 allocatable). On Kubernetes it runs with requests of 1 vCPU and 3.5 GB and limits of 2 vCPU and 4 GB on the t4g.large, and the two small apps share the nodes with about 1.5 GB of agents and system pods. That gap between requests and limits is a real Kubernetes advantage, because on Fargate you pay for the whole task. Even with it, EKS loses.

Dev, 2 regions, full stack: the part that depends on the orchestrator (USD a month)

ScenarioControl planeComputeSystem podsObservabilityTotal× ECS Spot
ECS Fargate arm64, on-demand (what prd would do)0.0079.300.004.0083.302.6
ECS Fargate arm64, Spot (dev and stg today)0.0027.750.004.0031.751.0
EKS managed node groups, Graviton, on-demand146.00102.910.0014.00262.918.3
EKS managed node groups, Graviton, Spot146.0044.040.0014.00204.046.4
EKS Auto Mode, on-demand146.00114.690.0014.00274.698.7
EKS Auto Mode, Spot (the fee on the on-demand price)146.0055.820.0014.00215.826.8
EKS Fargate x86, no Spot (IPv6 egress validated, no NAT)146.00107.2290.1034.00377.3211.9
EKS Fargate x86, with NAT (one regional NAT per region)146.00172.9290.1034.00443.0214.0
Control planeApp computeSystem podsObservability
ECS Fargate arm64, on-demand
what prd would do
83.30
ECS Fargate arm64, Spot
dev and stg today
31.75
EKS managed node groups, on-demand
262.91
EKS managed node groups, Spot
204.04
EKS Auto Mode, on-demand
274.69
EKS Auto Mode, Spot
the fee on the on-demand price
215.82
EKS Fargate x86, no Spot
IPv6 egress validated, no NAT
377.32
EKS Fargate x86, with NAT
one regional NAT per region
443.02
USD a month, dev, two regions, only the part that depends on the orchestrator: the table above, drawn. Hover a segment for its value.

Three environments, and the edge cases

ReadingECSEKS MNGEKS Auto ModeEKS Fargate
dev, stg and prd on (dev and stg on Spot where it exists, prd on-demand)146.80670.99706.331,131.96
One environment with “everything off” and the cluster kept (an empty ECS cluster costs nothing)0.00146.00146.00146.00
One environment with everything off and the cluster destroyed by lotr (the infrastructure apply takes 12 to 18 minutes longer, an estimate)0.000.000.000.00
A cluster left behind on its version (extended support), per cluster0.00+365.00+365.00+365.00
Only dev, one region (us-east-1), Spot where it exists: today’s daily use27.23112.63121.46231.64

The order of magnitude does not change with the assumptions. Even with Container Insights at zero and Spot at 70%, the cheapest EKS stays above USD 180 a month in dev with two regions, because USD 146 of it are the two control planes. In one region, the difference is a fixed USD 73 plus the smallest node, against a USD 2.52 Spot task.

7. Monitoring, item by item

What ecs-app delivers today, what would do the same on EKS, the effort, and whether the result is better, the same or worse. “Worse” here means more pieces to maintain for the same result, not that Kubernetes monitors badly.

Today on ECSEquivalent on EKSEffortResult
CPU and memory of the service (AWS/ECS, 2 widgets, 2 alarms)Container Insights enhanced through the CloudWatch agent (a DaemonSet) or the metrics-server metrics; on Fargate, ADOT reading the kubeletMedium: the dashboard and alarms rewritten with namespace and pod dimensionsbetter per pod and container, not only per service
Tasks running, desired and pending, network I/O, ephemeral storage (ECS/ContainerInsights, 3 widgets)pod_number_of_running_pods, pod_network_* and node_filesystem_* of EKS Container InsightsMediumsame
The ALB’s 15 widgets and 5 alarms (latency, errors, hosts, connections, TLS, requests per AZ)The same: Terraform still creates the TG, and the TargetGroupBinding only registers the podsNonesame
Target tracking autoscaling on CPU and memoryHPA v2 with metrics-server, minimum and maximum per envLowsame
Autoscaling on the ALB’s requests per targetKEDA with the CloudWatch scaler (one more operator, identity and version), or HPA custom metrics with the CloudWatch adapterHighworse
Emergency step scaling (+50% / +100%)No direct equivalent; the HPA’s behavior.scaleUp policies come closeLow, with a lossworse
The deploy circuit breaker and automatic rollbackRollingUpdate with a readiness probe and progressDeadlineSeconds; an automatic rollback needs Argo Rollouts or a pipeline stepMediumworse without Argo
Native BLUE_GREEN, CANARY and LINEAR strategies (no CodeDeploy)Argo Rollouts with traffic routing through the ALB (it needs an Ingress managed by the controller, which conflicts with the Terraform TG), or two TGs and two TargetGroupBindings with weights on the rulesHighworse
Logs: FireLens to stdout and S3, the account’s Athena with ecs_task_arn, ecs_task_definition, container_idA Fluent Bit DaemonSet with the kubernetes filter: the app’s columns unchanged, metadata kubernetes.namespace_name, pod_name, container_name, pod_id; the BigBang’s columns and its lines-per-task query changeMedium, in BigBang and in the modulesame with kubectl logs thrown in
Logs Insights widgets (Deploys / New Tasks per stream, level distribution, latest lines)The same queries over the Fluent Bit group, with a stream per pod instead of per taskLowsame
Tracing: the ADOT sidecar, X-Ray, trace.id on every log lineAn ADOT sidecar injected by the operator, or a collector per node; AwsEksResourceDetectorLowsame
The per-app cost widget (Cost Explorer filtered by the app_name tag, propagated from the task definition)Cost Explorer does not see the pod: on shared nodes the cost belongs to the node’s EC2. It takes Split Cost Allocation Data for EKS (CUR 2.0 through Data Exports, queried with Athena, with the pod’s namespace and labels as dimensions) or Kubecost / OpenCost. The current widget, which calls Cost Explorer filtered by the app_name tag, stops seeing the app’s costHighworse the widget breaks as it is
The secrets dashboard (CloudTrail: reads per role, failures, changes)The same, but the principal of the reads becomes the identity of the External Secrets Operator: the “who read it” metric loses its per-app detail, unless every app gets its own SecretStore with its own roleMediumworse
Alerts to SNS and SlackThe sameNonesame
ECS Execkubectl exec with access entries; auditing through the control plane’s audit log (paid)Lowsame
The container health check (CMD-SHELL true)Liveness, readiness and startup HTTP probes: no shell neededLowbetter it solves the FROM scratch status page
Cluster healthNew: nodes NotReady, pods Pending and CrashLoopBackOff, the version and add-ons to upgrade, the API server. None of it exists on ECS, because there is nothing there to breakMedium, newextra

Where EKS monitors better

  • Prometheus and Grafana are the ecosystem’s standard. Amazon Managed Service for Prometheus with the managed collector, and Amazon Managed Grafana, give metrics per pod, per namespace and per app, with ready-made dashboards (kube-prometheus-stack). The cost: AMP per ingested sample and AMG per active user, plus one more stack to keep versioned.
  • EKS Container Insights enhanced gives a view of pods, nodes and namespaces in the console, with more detail than the ECS one.
  • kubectl gives a live view (describe, logs, top, events) that on ECS takes the console or several CLI calls.

None of this is free or automatic: every piece is an add-on or a chart with its own version. ecs-app reaches today’s result with nothing but AWS resources in the app’s Terraform.

8. The sidecars in both options

FireLens today

  • An essential fluentbit container in every task, 50 MB reserved, its own image with s3-output.conf baked in (aws_fluent_bit_init_file_2), the LOG_S3_* variables.
  • It does two things: it prints every JSON record to awslogs (what the ECS console reads), and it sends gzip to logs/<app>/YYYY/MM/DD/ in the account’s bucket.
  • A failure of the S3 output takes the task down (essential).
  • The cost: memory in every task, and one more image to build and scan (SBOM).

Fluent Bit on EKS with nodes

  • One DaemonSet per cluster (the amazon-cloudwatch-observability add-on, or the aws-for-fluent-bit chart), one configuration for every app, read from a ConfigMap: the aio-<app>-fluentbit image and the build_sidecars step go away.
  • Memory per node (about 100 to 150 MB) instead of per task; the app keeps its 50 MB.
  • The kubernetes filter adds the namespace, the pod and the container; s3_key_format uses the namespace as <app> and keeps the Athena layout. The tag kube.<ns>.<pod>.<container> replaces FireLens’s $TAG.
  • A sick Fluent Bit affects the logs of every app on the node, not one task.
  • The stdout output stops making sense: kubectl logs reads the container’s stdout directly.

The built-in log router of EKS Fargate

  • Managed by AWS, with no pod to pay for, configured by an aws-logging ConfigMap of at most 5,300 characters, with Filter, Output and Parser sections only.
  • Allowed outputs: CloudWatch, Firehose, Kinesis and OpenSearch. No direct S3: the road to Athena becomes Firehose with dynamic partitioning (a cost per GB, and prefixes that differ from today’s layout).
  • No stdout, no extra plugins.

ADOT today

  • An opentelemetry container in every task, 128 MB reserved, ecs-xray.yaml, OTLP on localhost 4317 and 4318; the app gets the OTEL_* variables from the module.
  • It cannot pull from public.ecr.aws in the apps subnets, which have no NAT: that is why enable_otel_sidecar is off with app_network = apps.

ADOT on EKS

  • With nodes, two modes. The operator with sidecar injection (the annotation sidecar.opentelemetry.io/inject): the same design as today, 128 MB per pod, localhost. Or one collector per node (a DaemonSet), with the app pointing at the node’s IP (status.hostIP): memory per node, one more network hop, simpler to operate.
  • On Fargate, sidecar only (no DaemonSets), adding to the pod’s size and to the rounding.
  • The identity for X-Ray comes from the ServiceAccount’s Pod Identity, not from a task role.
  • The image still comes from public.ecr.aws, so the egress problem of the apps subnets is the same, solved by nodes in a public subnet or by mirroring the image to ECR.

The Dependency-Track case

  • wait-for-db becomes a literal initContainer; dependsOn HEALTHY becomes the apiserver’s readiness and startup probes, and the frontend simply waits for the Service.
  • The project publishes an official Helm chart, which on EKS would try to pull the app out of the tfmod-eks-app pattern. That decision has to be made before starting.

In short: with nodes, the log and trace sidecars can become cluster agents and give 178 MB back to every workload pod, at the cost of a shared point of failure and more pieces to upgrade. On Fargate, the design stays as it is today, more expensive and with fewer outputs.

9. Migration effort

Working days of focused work (a person plus Claude) to reach what exists today. ecs-app took months of iteration in the pipeline; the numbers below assume its code and tests are reused wherever they fit.

#DeliverableContentDays
1arglabs-tfmod-bigbang-eks-clusterThe cluster, access entries, add-ons, three compute modes, LB Controller, ESO, ADOT, alarms, a dashboard with the cost widget, tests5 to 8
2BigBang, IAM and Athenatfmod-eks-cluster.tf, the platform fields, eks:* on the deployer, the new columns and query of the log bucket2 to 3
3arglabs-tfmod-eks-appKubernetes objects through the provider, TargetGroupBinding, Pod Identity, ExternalSecret, HPA, the rewritten dashboard and alarms, tests6 to 10
4arglabs-workloadA parkable tfmod-eks-app.tf, the trust on the workload role, tracing.py, the configure skill, CLAUDE.md, TESTS.md, the validate matrix with 11 files3 to 4
5statuspageHTTP probes in place of the disabled health check1 to 2
6dependencytrackThe init container, probes, three containers, the Aurora secrets through ExternalSecret2
7sharedpipelinesThe eks-app flow, rollout status, the runner reaching the EKS endpoint, an image with kubectl1 to 2
8Validation, docs and blogEnd-to-end tests in dev, timings, measured costs, operations.md, a post3 to 5
Total23 to 36

Order and risks

  1. The cluster module in dev, one region, compute_mode = mng on Spot. Validate the network egress of the chosen subnets (ECR, STS, CloudWatch, X-Ray) before any app.
  2. BigBang with enable_eks_cluster in dev only; measure the apply and destroy times in lotr.
  3. tfmod-eks-app with the workload; repeat the ECSAPP001 and SECRETS001 tests on the EKS version.
  4. statuspage and dependencytrack last, the second one with the Helm chart versus module decision.
  • Network: nodes in a public subnet with a public IPv4 repeat today’s posture; IPv6-only apps subnets require validating the dual-stack endpoints. Fargate does not have the first option.
  • The runner and the EKS API: the runner is self-hosted and internal; the public endpoint with an allowlist is the simple path, while the private one requires the runner in the VPC.
  • Names: the 64-character limit of role names still applies; aio-workload-eks-cost-widget-lambda-role-use1-dev has 49.
  • Calendar: at least one Kubernetes version upgrade a year, with the add-ons aligned, or USD 365 more per cluster a month.
  • Two vocabularies in the same root: AWS resources and Kubernetes objects in the same apply works, but an unavailable cluster makes the whole plan fail.

10. Pros and cons

ECS, as it is

  • A free cluster, with no version and no add-ons: BigBang delivers an empty namespace and everything arrives with the app.
  • Spot per service (min_ondemand, spot_weight), arm64 end to end.
  • Monitoring built from AWS resources in the app’s Terraform: 24 widgets, alarms, a per-app cost widget, a secrets dashboard.
  • Native deploy strategies with rollback.
  • A clear cost boundary: a tag on the task definition, and Cost Explorer answers.
  • Cons: no Helm catalog, no operators, no namespaces for ephemeral environments (every branch is a stack), a smaller tool ecosystem, and less learning value for anyone who needs Kubernetes on the job market.

EKS

  • The market’s standard API, portability, Helm and operators (Dependency-Track itself has a chart), Argo CD as a managed capability (from USD 22 a month).
  • Namespaces as ephemeral environments: faster and cheaper than today.
  • Per-pod observability, and the Prometheus and Grafana ecosystem.
  • HTTP probes solve the images without a shell.
  • Requests smaller than limits pack pods into fewer nodes; on Fargate you pay for the whole task.
  • Cons: USD 73 per cluster a month before any pod (146 per environment in two regions); a version calendar; dozens of versioned pieces (CNI, coredns, kube-proxy, LB controller, metrics-server, ESO, ADOT, Fluent Bit, KEDA, Argo); the per-app cost widget stops working without Split Cost Allocation; Spot belongs to the node, not to the service; Fargate is x86 only, with no Spot; the deployer needs new permissions; two infrastructure vocabularies.

When EKS would be worth it

  • If most of what runs came from Helm charts or needed operators (databases in the cluster, queues, CRDs).
  • If there were dozens of services per account and environment: the control plane gets diluted, and ephemeral environments as namespaces pay for it.
  • If the team already spoke Kubernetes, and the platform were used by teams that expect kubectl.
  • If the goal were a lab and content: an EKS track in ARGLabs makes a good post, and its cost in dev with one region is close to USD 113 a month while it is on.

None of the four describes ARGLabs today: three small services, one person, daily use in dev only, and the project’s public thesis is precisely that ECS in the team’s own account makes the shared cluster unnecessary.

11. Verdict and recommendation

Do not migrate. Parity would cost 23 to 36 days of work, multiply the part of the cost that depends on the orchestrator by 6.4, rebuild 9 of the 24 widgets and break the per-app cost widget, all to operate a control plane with a calendar of its own. What EKS would do better (ephemeral environments as namespaces, probes, per-pod observability) does not make up for what is lost.

If EKS comes in, it should be a parallel track, not a migration. An enable_eks_cluster in BigBang, on in dev only and in one region, with a Graviton managed node group on Spot (option 1) or Auto Mode (option 2, less operations, 5% to 6% more expensive), and a parkable tfmod-eks-app.tf in the workload next to tfmod-ecs-app.tf. The same image, the same pipeline and the same ALB on both tracks is the most honest demonstration of the difference, and lotr switches the track off when it is not in use.

The incremental path, if it goes ahead

  1. arglabs-tfmod-bigbang-eks-cluster with compute_mode = mng and the minimal add-ons (vpc-cni, coredns, kube-proxy, pod identity, metrics-server, CloudWatch observability). No LB Controller yet.
  2. BigBang dev with the cluster in us-east-1; measure the apply, the destroy and the real cost for 30 days on the cost dashboard.
  3. The LB Controller and tfmod-eks-app with the workload, a TargetGroupBinding on the ext ALB; repeat the tests of TESTS.md on the EKS track.
  4. Only then decide about statuspage and dependencytrack, and write the post with measured numbers in place of the estimates here.

12. Sources and assumptions

  • Amazon EKS pricing: USD 0.10 per cluster-hour (standard), 0.60 (extended), the examples of the Auto Mode fee (12% of on-demand), capabilities (Argo CD at USD 0.03 an hour).
  • AWS Fargate pricing: x86 at 0.000011244 per vCPU-second and 0.000001235 per GB-second; arm64 at 0.0000089944 and 0.0000009889; Spot up to 70% off; a 1-minute minimum.
  • EKS on AWS Fargate, considerations: no DaemonSets, no Spot, private subnets only, IP targets only, and the table row “Can run workloads that require Arm processors: No”.
  • Fargate logging: the aws-logging ConfigMap, its sections and its allowed destinations.
  • EKS Auto Mode and its managed instances: the supported families (t4g included), “more than 1 CPU, instance size is not nano, micro or small”, a 21-day maximum lifetime, no SSH or SSM.
  • IPv6 on EKS clusters: the family is chosen at creation, IPv6-only pods with NAT on the node for IPv4, the LB Controller in IP mode.
  • TargetGroupBinding: registering pods in a target group created outside Kubernetes.
  • Kubernetes version lifecycle on EKS: 14 months of standard support; 1.34 until 2026-12-02, 1.35 until 2027-03-27, 1.36 until 2027-08-02 (the 1.35 and 1.36 dates came from secondary sources).
  • CloudWatch pricing: Container Insights enhanced for EKS at 0.21 per million observations; ECS enhanced at 0.07 per metric; custom metrics 0.30; alarms 0.10; dashboards 3.00; X-Ray 5.00 per million traces.
  • EC2 t4g.medium at USD 0.0336 an hour (us-east-1, on-demand, from price aggregators checked on 2026-10-14; check the AWS calculator before budgeting).
  • ARGLabs facts: arglabs-workload/CLAUDE.md and docs/operations.md, arglabs-tfmod-ecs-app, arglabs-tfmod-bigbang, arglabs-tfmod-bigbang-iam, arglabs-account-sharednetwork, arglabs-bigbang-aio, and the post Arglabs, by Claude, section “What it costs”.

Estimates, not prices: Container Insights per cluster, the control plane logs, Spot at 65% (Fargate) and 60% (EC2), the number of system pods on Fargate, the lines of Terraform of the new module and the days of effort. Data transfer, Route53 and the costs that are the same in both models were left out. Prices were read on the official pages on 2026-10-14; the model is a short Python script that recomputes the tables and the chart when an assumption changes.

Last updated on