Pulumi vs Crossplane: Which Infrastructure Tool to Use in 2026?
Pulumi vs Crossplane comparison — architecture, use cases, team fit, and when to use each for managing cloud infrastructure in 2026.
Both Pulumi and Crossplane manage cloud infrastructure. But they come from completely different philosophies — and choosing the wrong one for your context creates long-term pain.
Pulumi is an IaC tool. Crossplane is a Kubernetes-native control plane. They overlap in what they can provision but are built for different workflows and teams.
What Each Tool Is
Pulumi
Pulumi lets you write infrastructure code using real programming languages — Python, TypeScript, Go, Java, C#. No YAML, no HCL.
// TypeScript example
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("my-bucket", {
acl: "private",
versioning: { enabled: true },
tags: { Environment: "production" }
});
const distribution = new aws.cloudfront.Distribution("cdn", {
origins: [{
domainName: bucket.bucketRegionalDomainName,
originId: bucket.id,
}],
enabled: true,
// ...
});
export const bucketName = bucket.id;
export const cdnUrl = distribution.domainName;Pulumi feels like software development. You get loops, conditionals, functions, and the full power of your chosen language to model infrastructure.
Crossplane
Crossplane is a Kubernetes operator that manages cloud resources using the Kubernetes API — YAML manifests, CRDs, controllers, and reconciliation loops.
# Crossplane example — provision an RDS database
apiVersion: database.aws.upbound.io/v1beta1
kind: RDSInstance
metadata:
name: my-database
spec:
forProvider:
region: ap-south-1
instanceClass: db.t3.medium
engine: postgres
engineVersion: "15"
allocatedStorage: 20
dbName: myapp
username: admin
passwordSecretRef:
name: db-password
namespace: default
key: password
writeConnectionSecretToRef:
namespace: default
name: my-database-connCrossplane runs inside your Kubernetes cluster. It watches for these CRDs and provisions/reconciles the actual cloud resources continuously.
Architecture Comparison
Pulumi Architecture
Developer writes code (TypeScript/Python/Go)
→ pulumi up
→ Pulumi engine diffs desired vs current state
→ Calls cloud APIs to create/update/delete
→ Stores state in Pulumi Cloud or S3/Azure Blob/GCS
Pulumi is a CLI tool like Terraform. You run it manually or in CI/CD. It's imperative in feel (you call pulumi up) even though the underlying model is declarative.
Crossplane Architecture
Kubernetes cluster runs Crossplane operator
→ You apply YAML manifests (kubectl apply)
→ Crossplane controller reconciles continuously
→ Creates/updates cloud resources to match desired state
→ State lives in Kubernetes etcd (the cluster IS the state store)
Crossplane is always running, always reconciling. If someone manually changes a cloud resource, Crossplane will detect the drift and correct it on the next reconciliation cycle.
Feature Comparison
| Feature | Pulumi | Crossplane |
|---|---|---|
| Language | Python, TS, Go, Java, C# | YAML / Kubernetes CRDs |
| State storage | Pulumi Cloud, S3, GCS | Kubernetes etcd |
| Reconciliation | On pulumi up | Continuous (every ~30s) |
| Drift detection | Yes (on run) | Yes (continuous) |
| Cloud coverage | 120+ providers | 30+ providers (provider packages) |
| Custom abstractions | Functions, classes | CompositeResourceDefinitions (XRDs) |
| Multi-cloud | Yes | Yes |
| Testing | Full unit test support | Limited |
| Kubernetes resources | Yes (kubernetes provider) | Yes (native) |
| Learning curve | Low for devs, medium for ops | High (K8s + Crossplane concepts) |
| Self-service portals | Manual | Yes (built-in via K8s RBAC) |
Abstractions and Reuse
This is where both tools differentiate themselves from Terraform HCL.
Pulumi — ComponentResources
// Build a reusable VPC + EKS cluster component
class EksCluster extends pulumi.ComponentResource {
public readonly kubeconfig: pulumi.Output<string>;
constructor(name: string, args: EksArgs, opts?: pulumi.ResourceOptions) {
super("myorg:index:EksCluster", name, {}, opts);
const vpc = new awsx.ec2.Vpc(`${name}-vpc`, {
numberOfAvailabilityZones: 2,
}, { parent: this });
const cluster = new eks.Cluster(`${name}-cluster`, {
vpcId: vpc.vpcId,
subnetIds: vpc.privateSubnetIds,
instanceType: args.instanceType ?? "t3.medium",
desiredCapacity: args.nodeCount ?? 2,
}, { parent: this });
this.kubeconfig = cluster.kubeconfig;
}
}
// Use it:
const prod = new EksCluster("production", { nodeCount: 5 });
const staging = new EksCluster("staging", { nodeCount: 2 });Crossplane — Composite Resource Definitions (XRDs)
# Define a composite resource (abstract)
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xclusters.platform.example.com
spec:
group: platform.example.com
names:
kind: XCluster
plural: xclusters
claimNames:
kind: Cluster
plural: clusters
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
region:
type: string
nodeCount:
type: integer
---
# Composition: how XCluster maps to real resources
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: cluster-aws
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: XCluster
resources:
- name: eks-cluster
base:
apiVersion: eks.aws.upbound.io/v1beta1
kind: Cluster
# ... EKS config
- name: node-group
base:
apiVersion: eks.aws.upbound.io/v1beta1
kind: NodeGroup
# ... node group configCrossplane compositions are more complex but enable self-service — developers can kubectl apply a Cluster claim and get a full EKS cluster without knowing AWS internals.
Self-Service Infrastructure
This is Crossplane's killer feature. With XRDs:
# Developer applies this (they don't know about VPCs, node groups, etc.)
apiVersion: platform.example.com/v1alpha1
kind: Cluster
metadata:
name: my-team-cluster
namespace: team-alpha
spec:
region: ap-south-1
nodeCount: 3Kubernetes RBAC controls who can create what:
# team-alpha can create Clusters but not modify production infra
kind: Role
rules:
- apiGroups: ["platform.example.com"]
resources: ["clusters"]
verbs: ["get", "list", "create", "delete"]This pattern — platform as a product — is where Crossplane shines. Pulumi requires a wrapper service (Terraform Cloud, custom API) to achieve the same self-service experience.
When to Use Pulumi
- Your team writes code (Python/TypeScript) and finds YAML/HCL painful
- You need complex conditional logic, loops, and abstractions in infrastructure
- You want full unit test coverage for infrastructure
- You're migrating from Terraform and want more power
- You don't want to run Kubernetes just to manage infrastructure
Not a fit if: Your team is ops-heavy (not developers), or you're building a self-service platform.
When to Use Crossplane
- You're building an internal developer platform (self-service infrastructure for dev teams)
- Your infrastructure should be GitOps-native (kubectl apply, ArgoCD, FluxCD)
- You need continuous drift correction, not just on-demand runs
- You already run Kubernetes and want infra to live in the same system
- You want RBAC over who can provision what infrastructure
Not a fit if: You don't run Kubernetes, or your team doesn't understand K8s controllers and CRDs.
Can You Use Both?
Yes — and some large companies do. Pulumi handles the foundational infrastructure (VPCs, clusters, shared services), while Crossplane runs inside the cluster to provision application-level resources for teams.
But for most teams: pick one. Running both adds significant operational complexity.
Summary
| Pulumi | Crossplane | |
|---|---|---|
| Paradigm | IaC with real languages | Kubernetes-native control plane |
| Best for | Developer-friendly IaC | Platform engineering + self-service |
| State | External backend | Kubernetes etcd |
| Reconciliation | On-demand | Continuous |
| Self-service | Requires wrapper | Built-in via K8s RBAC |
| Learning curve | Low-medium | High |
| Verdict | Terraform alternative for devs | Platform teams building developer platforms |
If you're building a developer platform where teams can self-serve infrastructure, Crossplane is the right architecture. If you're a DevOps engineer who finds Terraform's HCL limiting and wants to write real code, Pulumi is the right move.
Experiment with both on DigitalOcean Kubernetes — $200 free credit. Spin up a cluster, install Crossplane, and compare it with a Pulumi TypeScript stack for the same infrastructure.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
YAML Engineering Is Dying — What Replaces It Will Change DevOps Forever
Why the era of hand-writing thousands of YAML lines is ending. CUE, KCL, Pkl, CDK8s, and general-purpose languages are replacing raw YAML for infrastructure configuration.
How to Use AI Agents to Automate Terraform Infrastructure Changes in 2026
AI agents can now plan, review, and apply Terraform changes from natural language. Here's how agentic AI is transforming infrastructure-as-code workflows.
Argo Rollouts vs Flagger — Which Canary Deployment Tool Should You Use? (2026)
Both Argo Rollouts and Flagger do progressive delivery on Kubernetes. Here's a detailed comparison of features, architecture, and when to pick each.