Crossplane: Managing Cloud Infrastructure with Kubernetes APIs

Philip Rehberger Oct 13, 2026 7 min read

Use the K8s control plane to provision cloud resources. Covers compositions and provider configuration.

Crossplane reframes infrastructure as code: instead of running Terraform or Pulumi from CI, you declare cloud resources as Kubernetes Custom Resources, and a Kubernetes controller reconciles them with the cloud. Want a Postgres database? Create an RDSInstance resource in your cluster. The Crossplane controller calls the AWS API and provisions it.

This sounds like a strange way to do IaC until you see what it unlocks at scale. This post is what Crossplane actually does, where it fits, and the patterns that emerge in production teams using it.

The Conceptual Shift

Traditional IaC (Terraform, Pulumi): you run a tool from CI, it talks to the cloud, infrastructure is created. State lives in S3 or similar.

Crossplane: Kubernetes is the state. Resources are Custom Resources. The controller continuously reconciles.

apiVersion: rds.aws.crossplane.io/v1beta1
kind: DBInstance
metadata:
  name: my-app-db
spec:
  forProvider:
    region: us-east-1
    dbInstanceClass: db.t3.medium
    engine: postgres
    engineVersion: "15.4"
    allocatedStorage: 20
  writeConnectionSecretToRef:
    name: my-app-db-conn
    namespace: my-app

Apply this to a cluster with the AWS Crossplane provider installed; an RDS instance gets created. Update the spec; Crossplane reconciles the difference. Delete the resource; the RDS instance is deleted.

This makes infrastructure feel like Kubernetes resources because it is Kubernetes resources.

What Crossplane Unlocks

The interesting features emerge from this design choice.

Continuous reconciliation. Drift detection comes for free. If someone modifies the resource outside Crossplane (clicks in the AWS console), Crossplane brings it back to declared state.

Compositions. You can define abstractions that wrap multiple cloud resources. A "PostgresDatabase" composition might create an RDS instance, a security group, a backup retention policy, and a CloudWatch alarm — all as one CR.

apiVersion: database.example.com/v1
kind: PostgresDatabase
metadata:
  name: my-app-db
spec:
  size: medium
  version: "15"
  team: platform

Application teams apply this simple resource. The Composition defines what RDS, security groups, etc., get created underneath. The complexity is hidden but still declarative.

Self-service. Developers create the high-level CR; the platform team owns the Composition. Developers cannot misconfigure the underlying resources because they cannot directly touch them.

Multi-cloud abstractions. Compositions can pick the cloud at apply time. A "Database" composition might create RDS on AWS, Cloud SQL on GCP, or Postgres on Azure based on a parameter.

How It Compares to Terraform

For "I want to provision an RDS instance," Terraform is simpler. One module, one apply, done.

For "I want every team to be able to provision standardized databases with one resource, and I want to enforce compliance, and I want drift detection without running anything on a schedule," Crossplane is dramatically better.

The tipping point is when infrastructure is consumed by many teams and the platform team needs to provide abstractions. That is when Crossplane's design pays off.

The Operational Cost

Crossplane is not free. Real costs:

  • A Kubernetes cluster to run it on (usually a management cluster, not the workload cluster)
  • Crossplane provider operators per cloud (AWS, GCP, Azure each have a provider)
  • Continuous reconciliation means continuous API calls to the cloud (the cloud account's API rate limit can become a concern)
  • Debugging is harder than Terraform — you read controller logs and resource status, not a clean apply output

For small teams, this overhead is not worth it. For platform teams serving many consumers, it is the abstraction that makes self-service possible.

A Composition Example

A platform team wants to offer "managed Postgres for any team." The Composition:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: postgresdatabase.composition
spec:
  compositeTypeRef:
    apiVersion: database.example.com/v1
    kind: PostgresDatabase
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.crossplane.io/v1beta1
        kind: DBInstance
        spec:
          forProvider:
            engine: postgres
            engineVersion: "15.4"
            allocatedStorage: 20
            multiAZ: true
            backupRetentionPeriod: 14
            storageEncrypted: true
            deletionProtection: true
    - name: security-group
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: SecurityGroup
        # ...
    - name: alarm
      base:
        apiVersion: cloudwatch.aws.crossplane.io/v1alpha1
        kind: MetricAlarm
        # ...

A team creating a PostgresDatabase gets:

  • Multi-AZ RDS with 14-day backups
  • Storage encryption
  • Deletion protection
  • A security group with the right rules
  • A CloudWatch alarm on disk space

They never write these directly. The Composition enforces standards.

Comparison with Other Approaches

Crossplane vs Terraform Modules. A Terraform module is shared code; multiple consumers run their own Terraform with the module. Crossplane is shared infrastructure; one controller reconciles all consumers' resources. Crossplane's centralization is better at scale; Terraform modules are simpler at small scale.

Crossplane vs Cloud Native Operators (RDS Operator, etc.). Each cloud has its own operators for specific services. Crossplane unifies them — one consistent API instead of N service-specific APIs. Cloud-native operators are simpler if you use only one cloud's service.

Crossplane vs AWS Service Catalog. Service Catalog is the AWS-specific version of "managed catalog of approved resources." Crossplane is cloud-agnostic and Kubernetes-native.

When Crossplane Wins

  • Platform teams serving multiple application teams
  • Multi-cloud or cloud-portable infrastructure
  • Strong self-service requirements with policy enforcement
  • Existing investment in Kubernetes-native operations
  • Standardization across many similar deployments

When Crossplane Loses

  • Single team, single application — Terraform is simpler
  • Infrequent infrastructure changes — the controller's continuous reconciliation has no payoff
  • No Kubernetes expertise on the team
  • Specific cloud features that lag in Crossplane providers

Many cloud-native features ship in the official provider SDKs (AWS Boto, AWS CDK) before they ship in Crossplane providers. If you need the latest feature, Crossplane may be behind.

The Provider Ecosystem

Crossplane providers exist for:

  • AWS, GCP, Azure (major clouds, most features)
  • Kubernetes (managing one cluster from another)
  • Helm (managing Helm releases as resources)
  • Terraform (the "Crossplane provider for Terraform" — wraps Terraform modules)

The third one is interesting: you can use existing Terraform modules through Crossplane. Get Terraform's ecosystem with Crossplane's reconciliation. Useful for migration.

Provider quality varies. The major-cloud providers are mature; smaller-provider ones are often in beta.

The "Crossplane on a Management Cluster" Pattern

The dominant production pattern:

  • One management cluster runs Crossplane
  • Workload clusters are managed by Crossplane (provisioned by Crossplane's Kubernetes provider)
  • Application infrastructure (databases, queues, storage) is also provisioned by Crossplane

The management cluster is the single point of provisioning. Every team's infrastructure flows through it.

The downside: the management cluster is a critical dependency. If it goes down, you cannot provision new resources (existing ones keep running).

Operational Patterns

A few that work:

Use GitOps. Argo CD or Flux syncs the Composition definitions and the CRs from git. Standard GitOps applies to Crossplane resources.

Use Compositions, not raw resources. Avoid having teams apply raw DBInstance resources. The whole point is the abstraction.

Treat Compositions like APIs. Version them. Document them. Deprecate carefully.

Monitor reconciliation. Crossplane's controller metrics show how often resources are out of sync. Surface in dashboards.

What Adoption Costs

A team adopting Crossplane meaningfully:

  • 2-4 weeks to stand up Crossplane on a management cluster
  • 2-4 weeks per significant Composition (Postgres, Redis, etc.)
  • Ongoing operational cost of the management cluster and provider operators
  • Learning curve for application teams (the abstractions look different from Terraform modules)

This is platform team work. Solo developers and small teams should not adopt Crossplane.

When It Pays Off

Crossplane pays off when:

  • Multiple teams need to provision similar infrastructure
  • Self-service with policy enforcement is a real goal
  • The platform team can own the abstractions for the long term

Teams that try Crossplane without the platform team commitment usually abandon it after a few months. The abstractions need ownership; nobody owning them means they rot.

A Pragmatic Recommendation

For a team starting fresh: Terraform unless you specifically need self-service abstractions and have a platform team to own them.

For a platform team building self-service: Crossplane is worth evaluating. The investment pays back if you have at least a dozen consumer teams.

For a hybrid environment: Crossplane for the infrastructure abstractions teams consume; Terraform for the cluster-level setup. The two coexist.


Considering Crossplane for a platform-team initiative and not sure if the investment will pay back? We help teams scope Kubernetes-native infrastructure adoption based on consumer team count and self-service needs. scopeforged.com

Share this article

Related Articles

Need help with your project?

Let's discuss how we can help you build reliable software.