The Question
Both cdk8s and Terraform let you write infrastructure as code. Both can manage Kubernetes resources. So which one should you use?
The honest answer: it depends on where your boundary is between cloud infrastructure and Kubernetes workloads. Here's how I think about it.
cdk8s: When Kubernetes Is Your Domain
cdk8s (Cloud Development Kit for Kubernetes) lets you write Kubernetes manifests in TypeScript, Python, or Go instead of YAML. You compile your code down to standard Kubernetes manifests.
We adopted cdk8s for a microservices migration. The win was immediate: instead of copy-pasting deployment YAML across 12 services and manually keeping them consistent, we had a Service construct that encoded our standards — resource limits, probes, service accounts — by default. Adding a new service meant importing the construct and overriding the fields that differed.
Where cdk8s shines:
- Large numbers of similar Kubernetes resources
- Teams that are already comfortable in TypeScript/Python
- You want real programming constructs (loops, conditionals, inheritance) rather than Helm's Go template DSL
- Your infra boundary is Kubernetes
Where it falls short:
- cdk8s doesn't manage AWS RDS, S3, IAM — anything outside the Kubernetes API
- No state management — the compiled manifests are applied with
kubectl applyor your CD tool - Ecosystem is smaller than Helm; fewer off-the-shelf charts
Terraform: When Your Boundary Is the Cloud
Terraform owns the cloud layer: VPCs, RDS instances, IAM roles, S3 buckets, EKS clusters, DNS records. It has a state backend that lets it reason about what exists and what needs to change.
With the Kubernetes and Helm providers, you can also manage Kubernetes resources from Terraform — but this gets awkward. Kubernetes has its own reconciliation loop; Terraform's plan/apply model doesn't map cleanly onto Kubernetes controllers. I've seen Terraform state get out of sync with actual cluster state in ways that required manual repair.
Where Terraform shines:
- Cloud infrastructure (AWS, GCP, Azure) — this is its native domain
- Multi-cloud or hybrid environments
- You need drift detection and state reconciliation across cloud resources
- Team has HCL muscle memory
Where it falls short:
- Managing Kubernetes application workloads with the k8s provider is awkward
- HCL's expressiveness limits (no real loops before Terraform 0.13, limited OOP)
- Large Terraform states become slow and operationally risky
How I Use Both
In practice, I use Terraform for the cloud layer and Helm or cdk8s for the Kubernetes layer, with a clean boundary between them:
Terraform: EKS cluster, node groups, VPC, RDS, IAM, ACM certs
↓ outputs cluster endpoint, credentials
Helm/cdk8s: Deployments, Services, Ingresses, CRDs, ConfigMaps
Terraform provisions the infrastructure and outputs the values that Kubernetes workloads need (e.g., the RDS endpoint as a Kubernetes secret). The Kubernetes layer is applied separately.
This separation keeps each tool in its domain, avoids state management hell, and lets the Kubernetes and cloud infrastructure teams move independently.
The Short Answer
- cdk8s: reaching for it when you have a lot of Kubernetes workload code and want real programming language constructs.
- Terraform: cloud infrastructure, full stop. Don't manage Kubernetes workloads from it.
- Helm: still the right choice for third-party Kubernetes applications (cert-manager, prometheus-operator, ingress controllers).
The tools aren't competing — they're complementary at different layers of the stack.