A developer needs a database for a new service. They open a ticket, the platform team adds a module to the Terraform repo, someone reviews the plan, someone applies it. Two days at best. Crossplane changes the route: the developer applies a manifest in their own namespace and a controller takes over. The difference is not ticket speed, it is the model. Terraform keeps a snapshot of state in a file and reconciles it on demand; Crossplane turns the Kubernetes API into a control plane and reconciles continuously.
What a control plane is made of
A Provider is a plugin that speaks to an external API: AWS, GCP, Cloudflare, GitHub. A Managed Resource is a low-level resource of that API mirrored as a Kubernetes object. Then the platform work starts: an XRD declares the schema of your own abstract type, and a Composition describes what that type expands into. The developer sees three fields instead of twenty RDS ones: size: medium, version: "16" and a name. Everything else is the platform team's contract, hidden inside the Composition.
What version two changed
Crossplane is out of incubation: the CNCF graduated the project in October 2025, the current line is v2.3 from May 2026, and releases follow a quarterly cycle. Version two rewrote the user-facing model, so any article where a developer creates a Claim is now out of date on that point.
- XRs are namespaced by default (
scope: Namespacedin anapiextensions.crossplane.io/v2XRD) and the separate Claim object is gone — the developer creates the XR right in their namespace. - Managed Resources became namespaced too, and providers gained API groups with
.m.in them, for examples3.aws.m.upbound.io/v1beta1. - A Composition can assemble any Kubernetes resource, so provider-kubernetes as a workaround for in-cluster objects is no longer needed.
- Native patch & transform (
mode: Resources) was removed; onlymode: Pipelinewith composition functions in Go, Python or KCL remains. Plain YAML is still available asfunction-patch-and-transform, andcrossplane beta convert pipeline-compositionmigrates the old ones. - Operations cover day-2: recurring jobs such as cleanup and audits are described as a resource instead of a CronJob bolted on the side.
What continuous reconciliation buys
Drift. Terraform learns about a change made by hand in the console when someone runs a plan — at the next release or on a weekly job. The Crossplane controller compares desired and actual all the time and pulls the resource back to the manifest with no human involved. The same property hands you day-2 operations for free: reconciliation, statuses and events use the mechanism you already know from Deployments.
Self-service. Access control is plain namespace RBAC, change history is the cluster audit log, delivery is ArgoCD syncing an XR like any other manifest. Terraform grows a wrapper such as Atlantis or Spacelift to get self-service, and somebody operates that wrapper too.
What it costs
"No state file, therefore better" shows up in every comparison and stays the most dishonest argument. The file is gone, the state is not: it lives in your cluster's etcd. Three consequences follow.
Observability. To the question "what is deployed and in what shape" Terraform answers with one command, Crossplane with a set of kubectl calls and tools like k9s or the crossplane CLI. Evidence at scale: the stateful plan-review workflow is proven on installations with thousands of resources, while public stories of that size are noticeably rarer for Crossplane. Recovery: the control-plane cluster becomes a production system, so etcd backups, the restore procedure and access are now your responsibility rather than a line about an S3 backend.
The learning curve is a separate line item. XRDs, Compositions and functions mean building a platform API, not writing HCL. You need a team that owns the contract, versions the schema and answers questions about it. Without that team Crossplane becomes the same Terraform, only harder to debug.
Where I draw the line
OpenTofu or Terraform for day 0: network, clusters, the IAM foundation, including the cluster that hosts the control plane. Crossplane for day 1 and beyond: databases, queues, buckets and repositories that teams order for their services every week. The lower layer changes rarely and deserves plan review, the upper one changes often and needs self-service. Picking between OpenTofu and Terraform does not shift that line — there is a separate piece on it.
Three mistakes you can spot immediately
- An XR without RBAC and namespace isolation: one developer gets the power to take down a neighbouring team's production.
- A Composition without
writeConnectionSecretsToNamespace: the database password lands somewhere other than where the application expects it. - A Composition of two dozen resources in plain YAML: a wall of patches nobody reads instead of a function with tests.
Crossplane wins not by having no state file, but by turning infrastructure into an API with a versioned schema and constant reconciliation. The price is a platform team that designs and maintains that API. Without one, staying on Terraform is the honest choice.