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.
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.
| App | Size | Regions | ALB | Sidecars | Spot | Particulars |
|---|---|---|---|---|---|---|
arglabs-workload | 0.25 vCPU / 512 MB, arm64 | us-east-1 (us-east-2 in the baseline) | ext, public subnets, Route53 health check | FireLens (own image with S3 output) and ADOT (X-Ray) | dev/stg 100%; prd 2 on-demand, then 50/50 | The example app; 334 MB left for the app after the sidecars |
arglabs-cloudteam-statuspage | 0.25 vCPU / 512 MB, arm64 | us-east-1 | int | none | same | Gatus, a FROM scratch image, no shell |
arglabs-cloudteam-dependencytrack | 2 vCPU / 4 GB, arm64 | us-east-1 | int | none | same | external_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
FARGATEandFARGATE_SPOTper 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 fromECS/ContainerInsights, 3 Logs Insights, and the Cost Explorer cost widget filtered byapp_nameandenv. - Sidecars: FireLens (Fluent Bit, its configuration baked into the
aio-<app>-fluentbitimage, 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
workloadrole, trustingecs-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
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.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.EKS proposed
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.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.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
TargetGroupBindingof the AWS Load Balancer Controller registers the pods in that TG, inipmode. 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
workloadrole gains thepods.eks.amazonaws.comtrust and anaws_eks_pod_identity_associationwith the app’s ServiceAccount. No IRSA, and no OIDC provider per cluster. - Secrets. The
SECRET_*stay inaio/<app>/<env>/app; anExternalSecret(External Secrets Operator) materializes them as a Kubernetes Secret, and the Deployment injects them as variables, keepingAPP_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-observabilityadd-on, or theaws-for-fluent-bitchart), with one configuration per cluster doing whats3-output.confdoes today: JSON to CloudWatch, and gzip tologs/<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
AwsEcsResourceDetectorforAwsEksResourceDetectorintracing.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_idEstimate: 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
| Where | What changes |
|---|---|
arglabs-tfmod-bigbang | A 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-iam | The 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-aio | enable_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-sharedpipelines | A 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-lotr | Nothing: 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 tests | kubernetes_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
| Criterion | Managed node groups (Graviton) | EKS Fargate | EKS Auto Mode |
|---|---|---|---|
| arm64 (the current images) | yes t4g, m7g, c7g | no x86 only, per the official EKS table | yes a custom arm64 NodePool |
| Spot | yes per node group | no | yes per NodePool; the 12% fee is not discounted |
| Minimum size | A t4g.small (2 vCPU, 2 GB) already runs; t4g.medium for the small apps, t4g.large for Dependency-Track | 0.25 vCPU / 0.5 GB per pod, plus 256 MB of overhead per pod, rounded up | More 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) | yes | no sidecars only; a built-in log router, to CloudWatch, Firehose, Kinesis or OpenSearch only | yes on immutable Bottlerocket nodes (no writing to /etc) |
| Subnets and NAT on the ARGLabs network | Nodes 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 CloudWatch | Like managed node groups |
| Container Insights | enhanced, per observation (about USD 3 to 10 for a small cluster) | enhanced not supported ADOT scraping the kubelet, custom metrics at USD 0.30 | enhanced |
| Add-ons included | vpc-cni, coredns, kube-proxy, pod identity; the rest is an add-on or Helm | The same, plus the log router | LB Controller, EBS CSI, CNI, Karpenter and Pod Identity, managed by AWS |
| Node and AMI upgrades | You trigger them (a node group rolling update) | AWS patches, with evictions | AWS, with a 21-day maximum node lifetime |
| Node access | SSM | There is no node | No SSH, no SSM |
| Cost beyond EC2 | The node’s EBS | Per pod, no Spot, with overhead | 12% on top of the instance’s on-demand price |
| Verdict for ARGLabs | option 1 if EKS | ruled out | option 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)
| Item | Price (USD) | Source and note |
|---|---|---|
| Fargate arm64 (ECS) | 0.03238 / vCPU-h; 0.00356 / GB-h | AWS 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-h | AWS Fargate pricing. Plus 256 MB per pod, rounded up to the next combination |
| EKS cluster | 73.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 / month | 0.0336 and 0.0672 / h. Spot modeled at 60% off. EBS gp3 20 GB: 1.60 per node |
| Auto Mode fee | 12% of on-demand | The examples on the EKS pricing page; not discounted by Spot or Savings Plans |
| NAT gateway | 32.85 / month per gateway | 0.045 / h, plus 0.045 / GB. Only in the Fargate scenario (private subnets required) |
| Container Insights | ECS 2.00; EKS 5.00; EKS Fargate 15.00 per cluster | Estimates: 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 logs | 2.00 per cluster | An estimate, at 0.50 / GB ingested, with api and audit on |
| System pods on EKS Fargate | 9.01 per pod | coredns ×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)
| App | ECS arm64 | ECS on-demand | ECS Spot | EKS Fargate x86 (pod rounded up) | EKS Fargate on-demand |
|---|---|---|---|---|---|
| workload (per region) | 0.25 vCPU / 0.5 GB | 7.21 | 2.52 | 0.25 vCPU / 1 GB | 10.63 |
| statuspage | 0.25 vCPU / 0.5 GB | 7.21 | 2.52 | 0.25 vCPU / 1 GB | 10.63 |
| dependencytrack | 2 vCPU / 4 GB | 57.67 | 20.18 | 2 vCPU / 5 GB | 75.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)
| Scenario | Control plane | Compute | System pods | Observability | Total | × ECS Spot |
|---|---|---|---|---|---|---|
| ECS Fargate arm64, on-demand (what prd would do) | 0.00 | 79.30 | 0.00 | 4.00 | 83.30 | 2.6 |
| ECS Fargate arm64, Spot (dev and stg today) | 0.00 | 27.75 | 0.00 | 4.00 | 31.75 | 1.0 |
| EKS managed node groups, Graviton, on-demand | 146.00 | 102.91 | 0.00 | 14.00 | 262.91 | 8.3 |
| EKS managed node groups, Graviton, Spot | 146.00 | 44.04 | 0.00 | 14.00 | 204.04 | 6.4 |
| EKS Auto Mode, on-demand | 146.00 | 114.69 | 0.00 | 14.00 | 274.69 | 8.7 |
| EKS Auto Mode, Spot (the fee on the on-demand price) | 146.00 | 55.82 | 0.00 | 14.00 | 215.82 | 6.8 |
| EKS Fargate x86, no Spot (IPv6 egress validated, no NAT) | 146.00 | 107.22 | 90.10 | 34.00 | 377.32 | 11.9 |
| EKS Fargate x86, with NAT (one regional NAT per region) | 146.00 | 172.92 | 90.10 | 34.00 | 443.02 | 14.0 |
Three environments, and the edge cases
| Reading | ECS | EKS MNG | EKS Auto Mode | EKS Fargate |
|---|---|---|---|---|
| dev, stg and prd on (dev and stg on Spot where it exists, prd on-demand) | 146.80 | 670.99 | 706.33 | 1,131.96 |
| One environment with “everything off” and the cluster kept (an empty ECS cluster costs nothing) | 0.00 | 146.00 | 146.00 | 146.00 |
| One environment with everything off and the cluster destroyed by lotr (the infrastructure apply takes 12 to 18 minutes longer, an estimate) | 0.00 | 0.00 | 0.00 | 0.00 |
| A cluster left behind on its version (extended support), per cluster | 0.00 | +365.00 | +365.00 | +365.00 |
| Only dev, one region (us-east-1), Spot where it exists: today’s daily use | 27.23 | 112.63 | 121.46 | 231.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 ECS | Equivalent on EKS | Effort | Result |
|---|---|---|---|
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 kubelet | Medium: the dashboard and alarms rewritten with namespace and pod dimensions | better 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 Insights | Medium | same |
| 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 pods | None | same |
| Target tracking autoscaling on CPU and memory | HPA v2 with metrics-server, minimum and maximum per env | Low | same |
| Autoscaling on the ALB’s requests per target | KEDA with the CloudWatch scaler (one more operator, identity and version), or HPA custom metrics with the CloudWatch adapter | High | worse |
| Emergency step scaling (+50% / +100%) | No direct equivalent; the HPA’s behavior.scaleUp policies come close | Low, with a loss | worse |
| The deploy circuit breaker and automatic rollback | RollingUpdate with a readiness probe and progressDeadlineSeconds; an automatic rollback needs Argo Rollouts or a pipeline step | Medium | worse 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 rules | High | worse |
Logs: FireLens to stdout and S3, the account’s Athena with ecs_task_arn, ecs_task_definition, container_id | A 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 change | Medium, in BigBang and in the module | same 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 task | Low | same |
Tracing: the ADOT sidecar, X-Ray, trace.id on every log line | An ADOT sidecar injected by the operator, or a collector per node; AwsEksResourceDetector | Low | same |
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 cost | High | worse 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 role | Medium | worse |
| Alerts to SNS and Slack | The same | None | same |
| ECS Exec | kubectl exec with access entries; auditing through the control plane’s audit log (paid) | Low | same |
The container health check (CMD-SHELL true) | Liveness, readiness and startup HTTP probes: no shell needed | Low | better it solves the FROM scratch status page |
| Cluster health | New: 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 break | Medium, new | extra |
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.
kubectlgives 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
fluentbitcontainer in every task, 50 MB reserved, its own image withs3-output.confbaked in (aws_fluent_bit_init_file_2), theLOG_S3_*variables. - It does two things: it prints every JSON record to
awslogs(what the ECS console reads), and it sends gzip tologs/<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-observabilityadd-on, or theaws-for-fluent-bitchart), one configuration for every app, read from a ConfigMap: theaio-<app>-fluentbitimage and thebuild_sidecarsstep 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_formatuses the namespace as<app>and keeps the Athena layout. The tagkube.<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 logsreads 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-loggingConfigMap 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
opentelemetrycontainer in every task, 128 MB reserved,ecs-xray.yaml, OTLP on localhost 4317 and 4318; the app gets theOTEL_*variables from the module. - It cannot pull from
public.ecr.awsin the apps subnets, which have no NAT: that is whyenable_otel_sidecaris off withapp_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-dbbecomes a literalinitContainer;dependsOn HEALTHYbecomes 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-apppattern. 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.
| # | Deliverable | Content | Days |
|---|---|---|---|
| 1 | arglabs-tfmod-bigbang-eks-cluster | The cluster, access entries, add-ons, three compute modes, LB Controller, ESO, ADOT, alarms, a dashboard with the cost widget, tests | 5 to 8 |
| 2 | BigBang, IAM and Athena | tfmod-eks-cluster.tf, the platform fields, eks:* on the deployer, the new columns and query of the log bucket | 2 to 3 |
| 3 | arglabs-tfmod-eks-app | Kubernetes objects through the provider, TargetGroupBinding, Pod Identity, ExternalSecret, HPA, the rewritten dashboard and alarms, tests | 6 to 10 |
| 4 | arglabs-workload | A 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 files | 3 to 4 |
| 5 | statuspage | HTTP probes in place of the disabled health check | 1 to 2 |
| 6 | dependencytrack | The init container, probes, three containers, the Aurora secrets through ExternalSecret | 2 |
| 7 | sharedpipelines | The eks-app flow, rollout status, the runner reaching the EKS endpoint, an image with kubectl | 1 to 2 |
| 8 | Validation, docs and blog | End-to-end tests in dev, timings, measured costs, operations.md, a post | 3 to 5 |
| Total | 23 to 36 |
Order and risks
- The cluster module in dev, one region,
compute_mode = mngon Spot. Validate the network egress of the chosen subnets (ECR, STS, CloudWatch, X-Ray) before any app. - BigBang with
enable_eks_clusterin dev only; measure the apply and destroy times in lotr. tfmod-eks-appwith the workload; repeat theECSAPP001andSECRETS001tests on the EKS version.- 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-devhas 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
arglabs-tfmod-bigbang-eks-clusterwithcompute_mode = mngand the minimal add-ons (vpc-cni, coredns, kube-proxy, pod identity, metrics-server, CloudWatch observability). No LB Controller yet.- 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.
- The LB Controller and
tfmod-eks-appwith the workload, a TargetGroupBinding on theextALB; repeat the tests ofTESTS.mdon the EKS track. - 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-loggingConfigMap, 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.mdanddocs/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.