Crossplane vs Terraform: An Honest Take After Using Both in Production
Let me start with something controversial: neither Crossplane nor Terraform is objectively better. Anyone who tells you otherwise is either selling something or hasn’t used both in production.
I’ve spent months building multi-cloud infrastructure with both tools, and I want to share what I’ve learned — including the things that vendors won’t tell you.
The Fundamental Difference
Before comparing features, you need to understand the philosophical difference:
Terraform: “Tell me what to create, and I’ll create it when you ask”
- You write code →
plan→ review →apply→ done - Changes happen when YOU decide
- State file tracks what exists
Crossplane: “Tell me what should exist, and I’ll make sure it always exists”
- You apply a manifest → controller watches → reconciles continuously
- Changes happen automatically when drift is detected
- Kubernetes etcd IS the state
This isn’t a minor detail. It fundamentally changes how you operate infrastructure.
Where Crossplane Shines
1. Platform Engineering
If you’re building an Internal Developer Platform, Crossplane’s Composition model is genuinely powerful:
# Developer's view - simple and cloud-agnostic
apiVersion: platform.mycompany.com/v1alpha1
kind: Database
metadata:
name: orders-db
spec:
engine: postgresql
size: small
Behind this simple claim, your Composition can create:
- RDS instance with proper security groups
- IAM roles with least-privilege access
- Secrets in AWS Secrets Manager
- Connection string in a Kubernetes Secret
Developers don’t need to know AWS. They just need a database.
2. GitOps Native
Crossplane works with ArgoCD or Flux out of the box. Your infrastructure is just Kubernetes manifests. No special integration needed.
3. Drift Prevention
If someone manually changes something in the console, Crossplane reverts it. This sounds great until it isn’t (more on that later).
Where Terraform Wins
1. Predictability
terraform plan
This command shows you EXACTLY what will change before it happens. No surprises. No “the controller decided to reconcile at 3 AM.”
With Crossplane, changes happen when the controller decides. You can check the status, but there’s no “plan” equivalent.
2. Debugging
Terraform error:
Error: creating S3 bucket: BucketAlreadyExists: The requested bucket name is not available.
Crossplane error:
kubectl describe bucket my-bucket
# Scroll through events
# Check conditions
# Check provider logs
# Trace through composite resource
# Find actual error buried somewhere
Terraform wins by a mile here.
3. Community and Ecosystem
Need to deploy something obscure? Terraform probably has a module. Stack Overflow has answers. Your colleague has used it before.
Crossplane is growing, but it’s not there yet.
The Reconciliation Problem (My Biggest Concern)
Here’s a scenario that keeps me up at night:
3 AM. Production is on fire. Traffic is 10x normal.
Your on-call engineer manually scales the ASG from 3 to 20 instances via the AWS console.
With Terraform: Nothing happens until someone runs terraform apply. The engineer can scale now and update the code tomorrow.
With Crossplane: The controller sees drift and reverts to 3 instances. Production goes down harder.
Yes, you can pause reconciliation:
kubectl annotate nodepool.aws.crossplane.io/my-pool crossplane.io/paused=true
But at 3 AM, under pressure, will your engineer remember this? Do they even have kubectl access to the management cluster?
This isn’t theoretical. It’s a real operational concern.
My Current Approach
After much experimentation, here’s where I’ve landed:
Use Crossplane for:
- Platform abstractions (Compositions that hide complexity)
- Resources that SHOULD never be manually changed
- Teams with strong Kubernetes expertise
- Environments where GitOps is already mature
Use Terraform for:
- Teams new to Kubernetes
- Resources that might need emergency manual intervention
- Complex multi-step provisioning with dependencies
- When you need predictable plan/apply workflow
Use both for:
- Platform engineering (Crossplane for the platform, Terraform for bootstrapping)
- Gradual migration from Terraform to Crossplane
- Teaching both paradigms to your team
What I’m Building
I’ve started a multi-cloud-iac-patterns repository that shows the same infrastructure patterns implemented in both Crossplane and Terraform.
Each pattern includes:
- Working code for AWS, GCP, and Azure
- Architectural Decision Records explaining trade-offs
- Honest comparison of how each tool handles the pattern
Conclusion
Stop asking “which is better” and start asking “which is better for MY context.”
Consider:
- Does your team live in Kubernetes already?
- How critical is drift prevention vs. emergency flexibility?
- What’s your debugging expertise level?
- Are you building a platform or just infrastructure?
There’s no wrong answer. There’s only the answer that fits your context.
Have questions or disagree with something? I’d love to hear your production experiences. Reach out on LinkedIn or open an issue on the repo.