Infrastructure as Code (IaC) has matured into several distinct models, each optimized for different teams, architectures, and operational constraints. Among the most widely used tools today are Terraform, Pulumi, and Crossplane. They overlap in some areas, but in practice they serve different purposes — and successful platform teams often combine them.
This post compares the three from an engineering perspective: how they work, where they fit best, and what patterns emerge when using them together.
Terraform
Terraform is the most established IaC tool and remains the default in many organizations. Its core idea is simple: describe infrastructure declaratively, track state, and apply changes predictably.
Strengths
-
Stable, predictable workflows
Terraform’s plan/apply model is mature and well understood. State backends, locking, and module versioning are reliable at scale. -
Largest provider ecosystem
Hundreds of official and community providers let teams manage everything from cloud primitives to SaaS systems. -
Clear separation of infrastructure and runtime
Terraform runs outside Kubernetes, which avoids circular dependencies and makes it suitable for foundational cloud resources. -
Strong compliance story
Declarative configs, policies (via Sentinel/Open Policy Agent), and controlled apply paths work well in enterprise environments.
Limitations
-
HCL can get unwieldy
Complex logic, dynamic resources, or data-driven patterns require workarounds that stretch HCL’s intended use. -
State management needs discipline
Teams must manage locks, backends, concurrent applies, and drift manually. -
Slow feedback loops
Terraform plans and applies are slower than Kubernetes reconciliation or programmatic IaC.
Best fit
- Base infrastructure (VPCs, IAM, networks, clusters)
- Teams that prioritize stability and broad provider support
- Environments where infra changes must be tightly controlled
Pulumi
Pulumi takes a fundamentally different approach: define infrastructure using general-purpose programming languages such as TypeScript, Go, Python, or C#. This unlocks expressiveness that declarative DSLs struggle with.
Strengths
-
Full programming model
You get loops, functions, tests, code reuse, conditional logic, typed APIs, and IDE support. -
Great for complex infrastructure logic
When resources depend on dynamic inputs, external APIs, or custom workflows, Pulumi’s model shines. -
Single tool for base + service infrastructure
Many teams use Pulumi end-to-end when they prefer code-driven workflows. -
Leverages Terraform’s provider ecosystem
Pulumi can bridge Terraform providers, reducing ecosystem fragmentation.
Limitations
-
Flexibility requires discipline
With great power comes the ability to create tangled abstractions quickly. -
State model still exists
Similar to Terraform, Pulumi uses a state backend (though typically more hidden). -
Not Kubernetes-native
Pulumi drives infra around Kubernetes, not from inside the cluster.
Best fit
- Teams comfortable with general-purpose languages
- Full-lifecycle infra management
- Complex, dynamic resource orchestration
- Infra tightly integrated with application code
Crossplane
Crossplane extends Kubernetes into an infrastructure control plane. It exposes cloud resources as CRDs and manages them through controllers and continuous reconciliation.
Strengths
-
Kubernetes-native IaC
Everything is a Kubernetes object with lifecycle managed by controllers. Drift is automatically corrected. -
Strong fit for platform engineering
Crossplane’s Composition API lets platform teams build internal abstractions (“Claim a database” → CRD creates network, subnet, DB, backup policy). -
Self-service without giving developers cloud credentials
Developers apply CRDs in namespaces; Crossplane provisions resources using platform-managed credentials. -
Great for service infrastructure
Databases, queues, buckets, caches — resources tightly linked to applications inside a cluster.
Limitations
-
Not ideal for foundational cloud infrastructure
It can’t bootstrap the Kubernetes cluster it runs on. -
Steeper learning curve
Understanding managed resources, claims, compositions, providers, and reconciliation boundaries takes time. -
Provider maturity varies
Much improved in the past years but still not as broad as Terraform’s ecosystem.
Best fit
- Kubernetes-native organizations
- Platform teams building internal developer platforms
- Self-service infrastructure
- Day-2 drift correction
Architectural Patterns That Work in Practice
In real-world engineering environments, these tools are often complementary.
Below are patterns that show up across platform teams.
Pattern 1 — Terraform for Base Infrastructure + Crossplane for Service Infrastructure
This is the most common layered approach.
Terraform handles:
- VPCs, subnets, gateways
- IAM
- Kubernetes clusters
- Global networking and org-level resources
Crossplane handles:
- Databases
- Queues
- Buckets
- Caches
- Internal abstractions (e.g.
CompositePostgresInstance)
Why this works
- Terraform is stable for foundational infra
- Crossplane enables day-2 management and self-service
- Kubernetes owners manage infra without exposing cloud credentials
- Drift is automatically corrected
This pattern aligns with industry recommendations without forcing a single-tool solution.
Pattern 2 — Pulumi for Everything
Pulumi is appealing when:
- teams want to define infra in familiar languages
- infrastructure logic is highly dynamic
- app and infra code live side by side
Pulumi can comfortably handle both:
- base infrastructure (networking, clusters)
- service infrastructure (databases, messaging, storage)
Caveat:
This works best when the team has strong engineering discipline — complex abstractions introduce long-term maintenance overhead.
Pattern 3 — Pulumi + Crossplane for K8s-Native Teams
A common pattern in organizations that operate Kubernetes at scale:
-
Pulumi provisions the foundational cloud environment
Clusters, networks, security boundaries, org resources. -
Crossplane manages service infra via API claims
Developer-facing resources reconciled by Kubernetes.
This provides:
- strong guardrails
- continuous reconciliation
- internal platform APIs
- full programmability where needed
Key Differences at a Glance
Execution Model
- Terraform: external engine (
plan/apply) - Pulumi: external engine using real languages
- Crossplane: Kubernetes control plane reconciliation
State
- Terraform/Pulumi: explicit state stored in a backend
- Crossplane: no external state; desired state is CRDs
Ecosystem support
- Terraform: broadest
- Pulumi: broad, plus Terraform bridges
- Crossplane: improving but more focused
Team fit
- Terraform: infra/platform teams
- Pulumi: software-engineering-oriented teams
- Crossplane: Kubernetes-first and platform-engineering teams
Selecting the Right Tool — Pragmatic Guidance
Choose Terraform when:
- you need the broadest provider ecosystem
- stability and clear workflows matter
- you’re provisioning foundational infrastructure
Choose Pulumi when:
- your team prefers real languages
- infrastructure requires dynamic logic
- you want infra+app in one environment
Choose Crossplane when:
- you’re building a Kubernetes-native platform
- you want self-service infrastructure
- you need continuous drift correction
Use a combination when:
- you want Terraform’s stability + Crossplane’s Kubernetes-native workflows
- you want Pulumi’s expressiveness + Crossplane’s platform APIs
- you want gradual, low-risk adoption across teams
There is no single “best” choice. The right tool depends on your architecture, your platform strategy, and how your teams work.
Summary
Terraform, Pulumi, and Crossplane represent three valid IaC philosophies:
- Terraform — stable declarative infra with broad provider coverage
- Pulumi — programmable IaC for dynamic and complex workflows
- Crossplane — Kubernetes-native control plane for platform engineering and self-service
Used thoughtfully, they complement each other. Many teams use Terraform for foundational infrastructure, Crossplane for managed service provisioning, and Pulumi for dynamic or code-driven workflows.
The goal isn’t choosing a winner — it’s selecting the right tool for each layer of the platform.