Skip to content

Crossplane Reference: K8s-Native Infra, Providers, Compositions & Self-Service Platform APIs

Crossplane is a CNCF-graduated project for K8s-native infrastructure as code. You write infrastructure (RDS instances, S3 buckets, VPCs) as Kubernetes CRDs, and Crossplane’s providers reconcile them against the actual cloud state — continuously, like K8s does for pods.

1. Crossplane vs Terraform vs Pulumi

When to choose Crossplane
Feature Crossplane Terraform Pulumi
Interface Kubernetes YAML + CRDs HCL files + CLI TypeScript/Python/Go code
State management K8s etcd (no separate state file) .tfstate file or Terraform Cloud Pulumi Cloud or self-hosted
Reconciliation Continuous (K8s controller loop) Manual: `terraform apply` Manual: `pulumi up`
GitOps Native — ArgoCD/Flux manages Crossplane manifests Needs additional GitOps layer Needs CI/CD pipeline
Drift detection Automatic — controller re-reconciles drift Manual: `terraform plan` Manual: `pulumi refresh`
Best for K8s-centric teams, GitOps-first, platform teams building self-service APIs Mature ecosystem, existing Terraform expertise Developers who want real programming languages for infra

2. Installation & Providers

Install Crossplane and configure a cloud provider
# Install Crossplane:
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm install crossplane crossplane-stable/crossplane   --namespace crossplane-system --create-namespace

kubectl get pods -n crossplane-system    # crossplane + rbac-manager pods

# Install a Provider (AWS example):
cat <

3. Managed Resources — Provision Cloud Infra

Create RDS, S3, and VPC resources via K8s manifests
# S3 bucket:
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: my-app-assets
  annotations:
    crossplane.io/external-name: my-app-assets-prod   # actual AWS bucket name
spec:
  forProvider:
    region: us-east-1
    acl: private
  providerConfigRef:
    name: default

# RDS PostgreSQL instance:
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
metadata:
  name: my-postgres
spec:
  forProvider:
    region: us-east-1
    instanceClass: db.t3.micro
    engine: postgres
    engineVersion: "15"
    dbName: myapp
    username: admin
    passwordSecretRef:
      namespace: default
      name: rds-password
      key: password
    allocatedStorage: 20
    skipFinalSnapshot: true
  providerConfigRef:
    name: default

# Check resource status:
kubectl get bucket my-app-assets
kubectl describe bucket my-app-assets   # shows SYNCED=True or any errors
kubectl get instance my-postgres        # shows READY=True once provisioned

# Get connection details (Crossplane writes to a K8s Secret):
kubectl get secret my-postgres-conn -o jsonpath='{.data.endpoint}' | base64 -d

4. Compositions — Build Self-Service Platform APIs

XRD + Composition: abstract multi-resource infra behind a simple API
# CompositeResourceDefinition (XRD) — defines your custom API:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.com
spec:
  group: database.example.com
  names:
    kind: XPostgreSQLInstance      # your custom kind
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance       # namespace-scoped "claim" your teams use
    plural: postgresqlinstances
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    storageGB: {type: integer}
                    size: {type: string, enum: [small, medium, large]}
                  required: [storageGB, size]

# Composition — maps your custom API to real cloud resources:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: postgres-aws
spec:
  compositeTypeRef:
    apiVersion: database.example.com/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.upbound.io/v1beta1
        kind: Instance
        spec:
          forProvider:
            region: us-east-1
            engine: postgres
            engineVersion: "15"
      patches:
        - fromFieldPath: spec.parameters.storageGB
          toFieldPath: spec.forProvider.allocatedStorage
        - fromFieldPath: spec.parameters.size
          toFieldPath: spec.forProvider.instanceClass
          transforms:
            - type: map
              map:
                small: db.t3.micro
                medium: db.t3.medium
                large: db.m5.large

# Developer claims a database (what platform consumers see):
apiVersion: database.example.com/v1alpha1
kind: PostgreSQLInstance     # uses the claim kind (namespace-scoped)
metadata:
  name: my-app-db
  namespace: production
spec:
  parameters:
    storageGB: 20
    size: small
  compositionSelector:
    matchLabels: {provider: aws}
  writeConnectionSecretToRef:
    name: my-app-db-conn    # K8s Secret with connection details
The power of Crossplane is the XRD + Composition abstraction layer: platform teams maintain the Composition (which cloud resources, which region, which security config), developers just claim a "PostgreSQLInstance" without knowing any AWS details.

5. Operations & Debugging

Check resource health, handle drift, and troubleshoot
# List all Crossplane-managed resources:
kubectl get managed                      # all Managed Resources (MRs) across providers
kubectl get composite                    # all Composite Resources (XRs)
kubectl get claim --all-namespaces       # all Claims

# Check a specific resource:
kubectl describe bucket my-app-assets
# Look for: SYNCED=True/False, READY=True/False
# Status conditions show exactly what failed

# Force re-sync (if stuck):
kubectl annotate managed my-resource crossplane.io/paused=true --overwrite
kubectl annotate managed my-resource crossplane.io/paused=false --overwrite

# Delete behavior — deletes actual cloud resource (unless policy=Orphan):
kubectl delete bucket my-app-assets      # DESTROYS the S3 bucket
# To delete K8s object but keep cloud resource:
kubectl annotate bucket my-app-assets crossplane.io/managementPolicies=Orphan --overwrite
kubectl delete bucket my-app-assets      # removes CRD object, leaves S3 bucket

# Crossplane logs:
kubectl logs -n crossplane-system -l pkg.crossplane.io/revision --tail=50
kubectl logs -n crossplane-system -l app=crossplane --tail=50

# Check provider version + health:
kubectl get providers
kubectl describe providerrevision        # shows installation status

Track Crossplane, Kubernetes, and infrastructure tool releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: ArgoCD & GitOps Reference | Flux v2 Reference | External Secrets Operator Reference | Kubernetes EOL Tracker

🔍 Free tool: K8s YAML Security Linter — check Crossplane XRD and Composition manifests for K8s security misconfigurations.

Founded

2023 in London, UK

Contact

hello@releaserun.com