Skip to content
Techsense Developers
TrustLet's Talk
Insights
Cloud & Infrastructure7 min readOct 3, 2026

Terraform vs. Pulumi: Which IaC Tool Fits Your Kubernetes Stack?

If your team already writes application code in TypeScript, Go, or Python and your engineers resist context-switching into HCL, the terraform vs pulumi decision usually breaks toward Pulumi. If you…

If your team already writes application code in TypeScript, Go, or Python and your engineers resist context-switching into HCL, the terraform vs pulumi decision usually breaks toward Pulumi. If you want a large ecosystem, a stable state model, and a declarative language that your entire organization can standardize on regardless of programming background, Terraform remains the safer default. That is the short answer. The rest of this post explains the tradeoffs that matter when the infrastructure you are managing is a Kubernetes stack, where the choice gets more nuanced than a generic infrastructure as code comparison would suggest.

The real problem you are solving

Most teams do not actually have a "which tool is better" problem. They have a "which tool fits our people, our existing modules, and our Kubernetes operating model" problem. Both Terraform and Pulumi can provision an EKS, GKE, or AKS cluster, wire up node pools, configure IAM, and deploy Helm charts. The difference shows up in three places:

  1. Who writes and reviews the code (platform engineers vs. application developers).
  2. How you manage state and drift across many clusters and environments.
  3. How you handle the boundary between provisioning infrastructure and deploying workloads into Kubernetes.

Let me take each tool on its own terms before comparing them head to head.

Terraform: declarative, mature, HCL-first

Terraform uses HashiCorp Configuration Language (HCL), a declarative domain-specific language. You describe the desired end state, and Terraform computes a plan to reach it.

A minimal EKS cluster and a Kubernetes namespace look like this:

module "eks" {
  source          = "terraform-aws-modules/eks/aws"
  version         = "~> 20.0"
  cluster_name    = "prod"
  cluster_version = "1.30"
  subnet_ids      = var.private_subnet_ids
  vpc_id          = var.vpc_id

  eks_managed_node_groups = {
    default = {
      instance_types = ["m5.large"]
      min_size       = 2
      max_size       = 6
      desired_size   = 3
    }
  }
}

resource "kubernetes_namespace" "apps" {
  metadata {
    name = "apps"
  }
}

Where Terraform is strong

  • Ecosystem breadth. The provider and module registry is large, and most cloud resources have well-maintained providers.
  • Readable diffs. terraform plan output is explicit about adds, changes, and destroys. For change-averse environments, this auditability matters.
  • Separation of concerns. HCL is intentionally limited. It is not a general-purpose language, which keeps infrastructure definitions constrained and reviewable by people who are not full-time developers.
  • State tooling. Remote state, state locking, and workspaces are well understood, and there is extensive operational knowledge in the market.

Where Terraform frustrates teams

  • Logic is awkward. count, for_each, and conditional expressions work, but complex control flow in HCL becomes hard to read quickly.
  • Testing is weaker. You can use terraform test, Terratest, or policy-as-code tools, but you are not writing unit tests in a language your developers already know.
  • Licensing shift. Terraform moved from MPL to the Business Source License (BSL) in 2023. That change prompted the OpenTofu fork under the Linux Foundation. If license terms affect your procurement, this is worth reviewing directly. (See the HashiCorp licensing announcement and the OpenTofu project.)

Pulumi: general-purpose languages, same desired-state model

Pulumi lets you define infrastructure in TypeScript, Python, Go, C#, or Java. Under the hood it still uses a desired-state engine and a state file, similar in spirit to Terraform. In fact, Pulumi can consume Terraform providers, which is why its resource coverage is broad.

The same EKS plus namespace in TypeScript:

import * as eks from "@pulumi/eks";
import * as k8s from "@pulumi/kubernetes";

const cluster = new eks.Cluster("prod", {
  version: "1.30",
  vpcId: vpcId,
  privateSubnetIds: privateSubnetIds,
  instanceType: "m5.large",
  minSize: 2,
  maxSize: 6,
  desiredCapacity: 3,
});

const provider = new k8s.Provider("k8s", {
  kubeconfig: cluster.kubeconfig,
});

new k8s.core.v1.Namespace("apps", {
  metadata: { name: "apps" },
}, { provider });

Where Pulumi is strong

  • Real language features. Loops, functions, classes, and package managers are native. If you need to generate fifty similar resources with branching logic, this is cleaner than HCL.
  • Developer familiarity. Teams fluent in TypeScript or Go do not learn a new syntax, only new libraries.
  • Testing. You can write unit and integration tests with the same frameworks you already use (Jest, pytest, Go's testing package).
  • Kubernetes ergonomics. Pulumi's Kubernetes provider can await resource readiness, apply CRDs, and transform manifests programmatically, which reduces glue scripting.

Where Pulumi requires caution

  • Power is a liability. A general-purpose language lets you write infrastructure code that is hard to review and easy to over-engineer. Discipline is required.
  • State backend. Pulumi defaults to its managed service for state. You can self-host with S3, Azure Blob, or GCS backends, but you should decide this deliberately rather than by default.
  • Smaller community. The ecosystem is active but smaller than Terraform's, so you may find fewer prebuilt modules for niche resources.

terraform vs pulumi for Kubernetes specifically

For Kubernetes stacks, the decision hinges on how you draw the line between provisioning and workload deployment.

The provisioning layer

Both tools provision clusters well. If your platform team is small and standardizing on one declarative language is a goal, Terraform's constrained HCL is an asset. If your platform engineers are also application developers, Pulumi removes a language boundary.

The workload deployment layer

This is where I urge caution with both tools. Using Terraform or Pulumi to manage long-lived in-cluster application state can create friction with GitOps controllers and Kubernetes-native reconciliation.

A common and pragmatic pattern:

  • Provision with IaC (Terraform or Pulumi): clusters, node pools, IAM, networking, and bootstrap controllers like the ingress controller, cert-manager, and a GitOps agent.
  • Deploy workloads with GitOps (Argo CD or Flux): application manifests and Helm releases reconciled continuously from Git.

This keeps your IaC tool focused on infrastructure that changes slowly, and lets Kubernetes do what it does best for workloads that change often. Both Terraform and Pulumi fit cleanly into this model.

A decision checklist

Choose Terraform (or OpenTofu) if:

  • Your team spans multiple backgrounds and you want one constrained language.
  • You rely heavily on the existing module registry.
  • Auditable, declarative plans are a compliance requirement.

Choose Pulumi if:

  • Your infrastructure authors are developers who want real programming constructs.
  • You need complex, dynamic resource generation.
  • You want to unit-test infrastructure logic in a familiar framework.

What about Terraform alternatives beyond Pulumi?

A complete infrastructure as code comparison should acknowledge other terraform alternatives:

  • OpenTofu: a drop-in-compatible fork, relevant if BSL licensing is a concern.
  • Crossplane: manages cloud resources as Kubernetes custom resources, appealing if you want a single control plane inside the cluster.
  • AWS CDK / CDKTF: code-first synthesis to CloudFormation or Terraform, useful in AWS-heavy shops.

Each solves a slightly different problem. For most Kubernetes stacks, the practical race is still terraform vs pulumi, with OpenTofu as a licensing-driven variant of the Terraform option.

How we approach the decision

When we help teams choose, we start from the operating model, not the tool. The questions we ask map directly to our cloud and infrastructure capabilities: who maintains this in a year, what your review process looks like, and how your Kubernetes delivery pipeline is structured. We also factor in sector constraints, because the compliance and change-control expectations we see across regulated and high-growth industries often tip the balance toward the more auditable, declarative option.

There is no universally correct answer. There is a correct answer for your team's skills, your governance needs, and your Kubernetes architecture.

FAQ

Can Pulumi use Terraform providers?

Yes. Pulumi bridges Terraform providers, which is a major reason its resource coverage is broad. You can access cloud resources that originated as Terraform providers directly from Pulumi code.

Is Terraform still free to use after the license change?

Terraform moved to the Business Source License in 2023, which restricts certain competitive commercial uses but remains usable for most teams. If the terms are a concern, OpenTofu is an open-source, MPL-licensed fork that is compatible with existing Terraform configurations.

Should I use Terraform or Pulumi to deploy Kubernetes workloads?

For most teams, use either tool to provision clusters and bootstrap controllers, then deploy application workloads with a GitOps tool like Argo CD or Flux. This keeps slow-changing infrastructure in IaC and fast-changing workloads under Kubernetes-native reconciliation.

Which tool is easier for developers to learn?

If your engineers already write TypeScript, Python, or Go, Pulumi has a gentler learning curve because there is no new syntax. If your team prefers a constrained, declarative language and values plan readability, Terraform's HCL is often easier to review.

Can I migrate from Terraform to Pulumi later?

Yes. Pulumi provides tooling to import existing resources and convert Terraform configurations, though complex module structures usually require manual review. Plan a migration as an incremental project rather than a single cutover.