Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 36: Crossplane: Infrastructure as CRDs

Crossplane extends Kubernetes’ reconciliation engine to any cloud resource — databases, storage buckets, DNS records, IAM roles — by representing each as a Kubernetes Custom Resource.

The Architecture

Crossplane installs as a set of controllers in your Kubernetes cluster. It extends the API server with CRDs that represent cloud resources, then reconciles those CRDs against the actual cloud state via provider plugins.

flowchart TD
    Claim["<b>Claim (XC)</b><br>I need a PostgreSQL DB,<br>medium size"]
    XR["<b>Composite Resource (XR)</b><br>cluster-scoped, created by<br>Crossplane from Claim"]
    Comp["Composition (template)<br>maps XR to managed resources"]

    Claim -->|Developer writes| XR
    XR --> Comp

    Comp --> MR1["<b>Managed Resource</b><br>RDS Instance<br>(provider-aws)"]
    Comp --> MR2["<b>Managed Resource</b><br>Subnet Group<br>(provider-aws)"]
    Comp --> MR3["<b>Managed Resource</b><br>Security Group<br>(provider-aws)"]

    MR1 --> AWS["<b>AWS API</b><br>Actual RDS instance, subnet group,<br>security group created and<br>continuously reconciled"]
    MR2 --> AWS
    MR3 --> AWS

Core Concepts

Providers

A Provider is a Crossplane package that installs CRDs and controllers for a specific cloud platform or service. provider-aws adds CRDs for RDS, S3, IAM, VPC, and hundreds of other AWS resources. provider-gcp, provider-azure, provider-helm, and provider-kubernetes do the same for their respective domains.

Providers authenticate to the cloud API using credentials stored in Kubernetes Secrets or via IRSA/Workload Identity.

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/upbound/provider-family-aws:v1.17.0

Managed Resources

A Managed Resource is a 1:1 representation of a cloud resource. One Managed Resource maps to exactly one external resource. The Crossplane controller for that resource type continuously reconciles: if the resource does not exist, create it. If it exists but has drifted from the spec, update it. If the Managed Resource is deleted, delete the cloud resource.

apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
metadata:
  name: my-database
spec:
  forProvider:
    region: us-east-1
    instanceClass: db.t3.medium
    engine: postgres
    engineVersion: "15"
    allocatedStorage: 20
    masterUsername: admin
    masterPasswordSecretRef:
      name: db-password
      namespace: crossplane-system
      key: password
  providerConfigRef:
    name: aws-provider

This is the lowest-level Crossplane abstraction. Platform teams rarely expose Managed Resources directly to developers — they are too detailed and cloud-specific.

Composite Resource Definitions (XRDs)

An XRD defines a new custom API — a higher-level abstraction that hides cloud-specific details. Think of it as defining a new Kubernetes resource type. The XRD specifies the schema (what fields developers can set) and optionally offers a namespaced Claim variant.

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqls.database.example.org
spec:
  group: database.example.org
  names:
    kind: XPostgreSQL
    plural: xpostgresqls
  claimNames:
    kind: PostgreSQL
    plural: postgresqls
  versions:
    - name: v1alpha1
      served: true
      revalidation: Strict
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: string
                  enum: ["small", "medium", "large"]
                version:
                  type: string
                  default: "15"
              required:
                - size

This XRD creates two new resource types: XPostgreSQL (cluster-scoped composite resource) and PostgreSQL (namespaced claim). Developers only interact with the claim.

Compositions

A Composition is the template that maps a Composite Resource to one or more Managed Resources. It is where the platform team encodes organizational opinions: which instance types correspond to “small,” “medium,” and “large,” what security groups to attach, what backup policies to apply.

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: postgresql-aws
spec:
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQL
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.upbound.io/v1beta2
        kind: Instance
        spec:
          forProvider:
            region: us-east-1
            engine: postgres
            publiclyAccessible: false
            storageEncrypted: true
            backupRetentionPeriod: 7
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.version
          toFieldPath: spec.forProvider.engineVersion
        - type: FromCompositeFieldPath
          fromFieldPath: spec.size
          toFieldPath: spec.forProvider.instanceClass
          transforms:
            - type: map
              map:
                small: db.t3.small
                medium: db.t3.medium
                large: db.r6g.xlarge

Claims

A Claim is the developer-facing interface. It is namespaced (unlike the Composite Resource), so it integrates naturally with team namespaces and RBAC. When a developer creates a Claim, Crossplane creates the corresponding Composite Resource, which the Composition expands into Managed Resources.

apiVersion: database.example.org/v1alpha1
kind: PostgreSQL
metadata:
  name: orders-db
  namespace: checkout-team
spec:
  size: medium
  version: "15"

Three lines of meaningful configuration. The developer does not need to know about RDS instance classes, security groups, subnet groups, or parameter groups. The platform team has encoded all of those decisions in the Composition.

Crossplane vs Terraform

Both Crossplane and Terraform manage cloud infrastructure declaratively. The differences are architectural:

AspectCrossplaneTerraform
Execution modelContinuous reconciliationOn-demand apply
State storageKubernetes etcd (CRDs)State files (S3, local, etc.)
Drift detectionAutomatic, continuousManual (terraform plan)
Drift correctionAutomaticManual (terraform apply)
Developer interfacekubectl, Kubernetes RBACCLI, separate auth
CompositionXRDs + Compositions (CRDs)Modules (HCL)
EcosystemGrowing, CRD-based providersMassive, mature provider ecosystem
Secret handlingKubernetes Secrets, nativeState file (encrypted via backend configuration; e.g., S3 SSE, Terraform Cloud encryption at rest)

Crossplane’s advantage: Continuous reconciliation means drift is detected and corrected automatically. If someone manually changes an RDS instance’s configuration via the AWS console, Crossplane will notice and revert it on the next reconciliation cycle (typically 1–10 minutes). Terraform only detects drift when someone runs terraform plan.

Terraform’s advantage: Maturity, ecosystem breadth, and the terraform plan workflow that lets teams review changes before applying them. Crossplane’s reconciliation model means changes to a Composition apply immediately to all resources that use it — there is no “plan” step.

In practice, many organizations use both: Terraform for foundational infrastructure (VPCs, IAM, Kubernetes clusters) managed by a platform team with manual review, and Crossplane for application-level resources (databases, caches, queues) managed self-service by development teams.

The Universal Control Plane Vision

Crossplane’s long-term vision is the “universal control plane” — a single Kubernetes API server that manages everything: containers, cloud resources, SaaS services, and internal tooling. Instead of developers learning kubectl for Kubernetes, the AWS console for cloud resources, and a CI tool’s web interface for pipelines, they interact with a single API that accepts declarative manifests for all of it.

Provider coverage is broad but not total. Complex multi-resource dependencies (create VPC, then subnet, then security group, then RDS instance) require careful ordering in Compositions. Error messages from failed cloud API calls can be opaque. But the trajectory is clear: the Kubernetes resource model is becoming the universal interface for infrastructure management, and Crossplane is the primary vehicle for that expansion.

Common Mistakes and Misconceptions

  • “Crossplane replaces Terraform.” See the comparison table above. Many organizations use both: Terraform for foundational infrastructure, Crossplane for application-level self-service.
  • “Compositions apply changes immediately with no review.” This is actually true and often a surprise. Unlike Terraform’s plan/apply workflow, changing a Composition affects all resources using it immediately. Use Composition revisions and staged rollouts.
  • “Crossplane providers cover every cloud resource.” Coverage is broad but not complete. Check the provider’s CRD list before committing to Crossplane for a specific resource. Some niche services may need Terraform or direct API calls.

Further Reading


Next: Multi-Tenancy — Namespace isolation, hierarchical namespaces, vCluster, and when soft boundaries are not enough.