Back to Technology

Crossplane Track Part 2: Compositions & XRDs

June 6, 2026 Wasil Zafar 40 min read

Crossplane Compositions define reusable infrastructure blueprints. XRDs expose a simplified platform API for consumers.

Table of Contents

  1. Why Compositions
  2. CompositeResourceDefinition (XRD)
  3. Composition
  4. Patches
  5. Testing Compositions
  6. Exercises
  7. Key Takeaways & Next Steps

Why Compositions

Individual Managed Resources give you fine-grained cloud control, but they expose too much complexity to application developers. A team requesting a database shouldn't need to specify VPC subnet IDs, security group rules, and parameter groups. Compositions solve this by defining opinionated infrastructure blueprints.

Platform Engineering Pattern: Platform teams define Compositions (blueprints) + XRDs (APIs). Application teams consume Claims (simple requests). This separates infrastructure expertise from application development.
  • Abstract complexity — Hide VPC, subnet, security group details behind a simple "give me a database" API
  • Enforce standards — Compositions embed organizational requirements (encryption, tagging, networking) that can't be bypassed
  • Enable self-service — Dev teams request infrastructure through simple Claims without cloud expertise

CompositeResourceDefinition (XRD)

An XRD defines the API schema — what parameters platform consumers can set. It registers a new CRD in the cluster (e.g., XDatabase) with a validated OpenAPI schema:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xdatabases.platform.example.com
spec:
  group: platform.example.com
  names:
    kind: XDatabase
    plural: xdatabases
  claimNames:
    kind: Database
    plural: databases
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    engine:
                      type: string
                      enum: ["postgres", "mysql"]
                      description: "Database engine type"
                    size:
                      type: string
                      enum: ["small", "medium", "large"]
                      description: "Instance size class"
                    region:
                      type: string
                      default: "us-east-1"
                  required:
                    - engine
                    - size
              required:
                - parameters
# Apply the XRD
kubectl apply -f xrd-database.yaml

# Verify the new CRDs are registered
kubectl get crd | grep platform.example.com
# xdatabases.platform.example.com   2026-06-06T10:00:00Z
# databases.platform.example.com    2026-06-06T10:00:00Z

# The XRD creates both:
# - XDatabase (cluster-scoped composite resource)
# - Database (namespace-scoped claim)

Composition

A Composition wires the XRD to actual Managed Resources. It specifies which cloud resources to create and how to map parameters from the composite resource to each underlying resource:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xdatabases.aws.platform.example.com
  labels:
    provider: aws
    engine: postgres
spec:
  compositeTypeRef:
    apiVersion: platform.example.com/v1alpha1
    kind: XDatabase
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.upbound.io/v1beta2
        kind: Instance
        spec:
          forProvider:
            engine: postgres
            engineVersion: "15"
            publiclyAccessible: false
            storageEncrypted: true
            storageType: gp3
            skipFinalSnapshot: true
            tags:
              ManagedBy: crossplane
              Platform: "true"
          providerConfigRef:
            name: default
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.region
          toFieldPath: spec.forProvider.region
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.size
          toFieldPath: spec.forProvider.instanceClass
          transforms:
            - type: map
              map:
                small: db.t3.micro
                medium: db.t3.medium
                large: db.r6g.large

Patches

Patches are the glue between composite resources and managed resources. They copy, transform, and compute values flowing between the API layer and the infrastructure layer.

Patch Types: FromCompositeFieldPath (composite → managed), ToCompositeFieldPath (managed → composite status), CombineFromComposite (merge multiple fields), and FromEnvironmentFieldPath (from EnvironmentConfig).
# Advanced patch examples within a Composition resource
patches:
  # Simple field copy
  - type: FromCompositeFieldPath
    fromFieldPath: spec.parameters.region
    toFieldPath: spec.forProvider.region

  # Map transform (enum to cloud value)
  - type: FromCompositeFieldPath
    fromFieldPath: spec.parameters.size
    toFieldPath: spec.forProvider.instanceClass
    transforms:
      - type: map
        map:
          small: db.t3.micro
          medium: db.t3.medium
          large: db.r6g.large

  # String format (combine values)
  - type: CombineFromComposite
    combine:
      variables:
        - fromFieldPath: metadata.name
        - fromFieldPath: spec.parameters.engine
      strategy: string
      string:
        fmt: "%s-%s-sg"
    toFieldPath: spec.forProvider.name

  # Propagate status back to composite
  - type: ToCompositeFieldPath
    fromFieldPath: status.atProvider.endpoint
    toFieldPath: status.endpoint

Testing Compositions

Crossplane provides a beta render command that locally renders a Composition against an XR (composite resource) input, showing exactly which Managed Resources would be created without touching any cloud API:

# Create a test XR input file (xr.yaml)
cat <<EOF > xr.yaml
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
metadata:
  name: test-db
spec:
  parameters:
    engine: postgres
    size: medium
    region: eu-west-1
EOF

# Render the Composition locally
crossplane beta render xr.yaml composition.yaml functions.yaml

# Output shows the rendered Managed Resources with all patches applied
# Verify instanceClass=db.t3.medium, region=eu-west-1, etc.

# Validate XRD schema against an input
crossplane beta validate xrd.yaml xr.yaml

Exercises

Exercise 1: Create an XRD called XNetwork that exposes parameters for cidrBlock (string) and availabilityZones (integer, 2 or 3). Write a Composition that creates a VPC and the specified number of subnets. Use a map transform to convert AZ count to subnet CIDR ranges.
Exercise 2: Extend the XDatabase Composition from this article to include a SecurityGroup resource and a SubnetGroup resource alongside the RDS Instance. Use CombineFromComposite patches to generate unique names for each resource. Add a ToCompositeFieldPath patch that exposes the RDS endpoint in the composite's status.
Exercise 3: Use crossplane beta render to test your Composition locally. Create three different XR inputs (small/postgres, medium/mysql, large/postgres) and verify the rendered output has the correct instance classes, engine versions, and generated names. Fix any patch errors the render reveals.

Key Takeaways & Next Steps

  • Compositions combine multiple Managed Resources into reusable infrastructure blueprints
  • XRDs define the simplified API schema that platform consumers interact with
  • Patches copy and transform values between the composite API and underlying resources
  • Map transforms convert human-friendly enums (small/medium/large) to cloud-specific values
  • crossplane beta render enables local testing without cloud API calls

Next in the Series

In Part 3: Claims & GitOps Integration, we'll use namespace-scoped Claims for self-service infrastructure requests and integrate Crossplane with Flux and ArgoCD for GitOps-driven provisioning.