Cleaning up in the wrong order is the number-one cause of surprise bills. The two ALBs and the PVC EBS volumes keep costing money even when you think the cluster is gone. Follow the exact order below and run the verification checklist at the end.
Principle: clean up in reverse order of how you built it. The key point is to release the
resources Kubernetes created (ALB, EBS) BEFORE terraform destroy — otherwise the AWS Load
Balancer Controller gets deleted with the cluster and never cleans up the ALBs, leaving orphaned
load balancers that make terraform destroy fail while you keep paying.
If you changed code, make sure your local copy is up to date (or delete and re-clone for a clean state):
cd vprofile-app && git pull
cd ../vprofile-helm && git pull
cd ../vprofile-infra && git pull
Identify the two load balancers that must disappear (one for vProfile, one for ArgoCD):
# Console: EC2 > Load Balancers (correct region). Or CLI:
aws elbv2 describe-load-balancers \
--query "LoadBalancers[].{Name:LoadBalancerName,DNS:DNSName}" --output table
Delete the Ingresses first so the AWS Load Balancer Controller removes both ALBs (while the controller is still alive):
kubectl get ingress -A
kubectl delete ingress vpro-ingress -n vprofile
kubectl delete ingress argocd-ingress -n argocd
Delete the vProfile workload so the PVC is removed → the EBS CSI driver deletes the MySQL EBS
volume (reclaim policy Delete). The simplest way is to delete the namespace (or the ArgoCD
Application):
kubectl delete ns vprofile
kubectl get pvc -A # must be empty, no PVC left hanging
Wait until both ALBs are gone (re-check EC2 > Load Balancers). If after a few minutes an ALB is
still there or an Ingress won’t delete, delete the ALB manually in the AWS Console — otherwise
terraform destroy gets stuck on the subnet/VPC because of the load balancer’s ENIs.
This service account was created with eksctl (plus a CloudFormation stack). Delete it with
delete:
eksctl delete iamserviceaccount \
--cluster vprofile-eks-cluster \
--namespace kube-system \
--name aws-load-balancer-controller
This takes a little while (it also deletes the matching CloudFormation stack). You may keep the IAM
policy AWSLoadBalancerControllerIAMPolicy — it costs nothing; delete it later if you want.
terraform destroy (delete EKS, VPC, node group)cd vprofile-infra
terraform init # optional, run it to be safe if you changed code
terraform destroy
This deletes the EKS control plane, node group (EC2 workers), VPC, public subnets, route tables and
the Internet Gateway. (This setup uses public subnets, with no NAT Gateway/Elastic IP, so those
two are nothing to worry about.) Wait for it to finish and confirm Destroy complete!.
Want to rebuild for practice? You don’t need to start from scratch: just terraform apply, then
continue from the iamserviceaccount step in the EKS prep section (the IAM policy already exists).
Recommended: Stop, don’t Terminate. Rebuilding SonarServer is some effort. Go to EC2 > Stop
instance to avoid compute charges; Start it again when you practice (remember to update
SONAR_HOST_URL because the public IP changes after each Stop/Start).
If you are sure you won’t use it again:
SonarServer instance.sonar-sg security group.Note: a Stopped EC2 instance is still charged for its EBS root volume (small), but not for compute.
github-actions and terraform-admin.vprofile and argocd at your domain provider.AWSLoadBalancerControllerIAMPolicy.Go through each item in the AWS Console (in the region you used):
| Service | Check | Costs money if missed |
|---|---|---|
| EC2 > Load Balancers | both ALBs gone | ALB billed hourly |
| EC2 > Volumes (EBS) | no available/orphaned volume (the PVC’s gp2) | EBS billed per GB |
| EC2 > Instances | workers deleted; SonarServer Stopped/Terminated | compute |
| EKS > Clusters | vprofile-eks-cluster gone | control plane hourly |
| CloudFormation | eksctl-* stacks deleted | (drags resources along) |
| ECR | repo deleted if unused | image storage |
| IAM | access keys/PAT revoked | security risk |
The two most-forgotten money sinks: leftover ALBs (from an Ingress that failed to delete) and orphaned EBS volumes from a PVC that wasn’t deleted before destroying the cluster. Always double-check these two last.