Crossplane: Kubernetes for Everything
- Universal Control Plane: Crossplane extends Kubernetes into a unified control plane for managing external infrastructure (databases, message queues, storage buckets, DNS records) alongside Kubernetes-native resources, all through the same API.
- Resource Abstraction with Compositions: Crossplane allows platform teams to define higher-level abstractions (Composite Resources) that encapsulate provider-specific details, enabling developers to provision infrastructure with simple, opinionated YAML without needing cloud console access or deep cloud expertise.
- Provider Ecosystem: Crossplane supports AWS, GCP, Azure, and dozens of other providers through a plugin model. Each Provider installs CRDs and controllers that map Kubernetes resources to cloud API calls.
- Declarative Infrastructure with Reconciliation: Infrastructure is defined and continuously reconciled just like any other Kubernetes resource. If someone manually changes a resource in the cloud console, Crossplane detects the drift and corrects it back to the declared state.
- GitOps-Native: Because all infrastructure is expressed as Kubernetes YAML, Crossplane integrates naturally with GitOps workflows (ArgoCD, Flux), giving you version-controlled, auditable infrastructure changes with pull request reviews.
Your application runs in Kubernetes, but your database, cache, and message queue are probably outside the cluster, managed by your cloud provider (AWS RDS, GCP Cloud SQL, Azure Cache for Redis). This means your team manages infrastructure through two completely different workflows: kubectl for the cluster and Terraform/CloudFormation/Console clicks for everything else.
Crossplane eliminates this split by turning your Kubernetes cluster into a universal control plane. You manage cloud resources using the same Kubernetes API, the same YAML manifests, and the same reconciliation loop that manages your Pods and Services.
1. Kubernetes for All Your Infrastructure
Visualize how you can provision an AWS RDS instance by simply applying a Kubernetes manifest:
Kubernetes Manifest
AWS Cloud
2. Crossplane Architecture
Crossplane is built on the Kubernetes operator pattern and consists of several layers:
Providers
Providers are the bridge between Crossplane and external APIs. Each Provider installs a set of CRDs and controllers that know how to manage resources for a specific platform. When you install provider-aws, you get CRDs like RDSInstance, S3Bucket, VPC, and hundreds more. The Provider's controllers watch for instances of these CRDs and make the corresponding AWS API calls to create, update, or delete the real resources.
# Install the AWS Provider
kubectl crossplane install provider xpkg.upbound.io/upbound/provider-aws-s3:v1.1.0
kubectl crossplane install provider xpkg.upbound.io/upbound/provider-aws-rds:v1.1.0
Managed Resources (MRs)
Managed Resources are the 1:1 representations of external cloud resources. Each MR maps directly to a single cloud API resource. When you create a Managed Resource in Kubernetes, the Provider controller provisions the real resource in the cloud. When you delete it, the real resource is deleted. When you update the spec, the real resource is updated.
# managed-rds.yaml
# Provisions an actual AWS RDS PostgreSQL instance via the Kubernetes API
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
metadata:
name: production-postgres
spec:
forProvider:
region: us-east-1
instanceClass: db.t3.medium
engine: postgres
engineVersion: "15.4"
allocatedStorage: 100
storageType: gp3
dbName: myapp
masterUsername: admin
masterPasswordSecretRef: # Reference a Kubernetes Secret for the password
name: rds-master-password
namespace: crossplane-system
key: password
publiclyAccessible: false
vpcSecurityGroupIdRefs:
- name: rds-security-group
dbSubnetGroupNameRef:
name: rds-subnet-group
backupRetentionPeriod: 7
multiAz: true # Enable Multi-AZ for high availability
storageEncrypted: true
writeConnectionSecretToRef: # Crossplane writes connection details here
name: production-postgres-conn
namespace: app-namespace
providerConfigRef:
name: aws-provider-config
Composite Resource Definitions (XRDs)
XRDs are the abstraction layer that makes Crossplane powerful for platform engineering. Instead of exposing raw cloud resources to developers, platform teams define higher-level abstractions like "Database" or "Cache" that hide provider-specific details.
An XRD defines a new custom API type. A Composition then implements that API by mapping it to one or more Managed Resources. Multiple Compositions can satisfy the same XRD, allowing different implementations for different environments or clouds.
# xrd-database.yaml
# Defines a new "Database" API in Kubernetes
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: databases.platform.example.com
spec:
group: platform.example.com
names:
kind: Database
plural: databases
claimNames: # Namespaced version for developers
kind: DatabaseClaim
plural: databaseclaims
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
size:
type: string
enum: ["small", "medium", "large"]
description: "T-shirt size for the database instance"
engine:
type: string
enum: ["postgres", "mysql"]
default: "postgres"
version:
type: string
default: "15"
required:
- size
Compositions
A Composition implements an XRD by defining which Managed Resources to create and how to map the abstract parameters (like size: medium) to concrete cloud parameters (like instanceClass: db.t3.medium).
# composition-database-aws.yaml
# Maps the abstract "Database" API to concrete AWS RDS resources
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: database-aws
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: Database
resources:
- name: rds-instance
base:
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
spec:
forProvider:
region: us-east-1
engine: postgres
storageType: gp3
publiclyAccessible: false
storageEncrypted: true
backupRetentionPeriod: 7
patches:
- type: FromCompositeFieldPath
fromFieldPath: "spec.size"
toFieldPath: "spec.forProvider.instanceClass"
transforms:
- type: map
map:
small: db.t3.micro
medium: db.t3.medium
large: db.r6g.xlarge
- type: FromCompositeFieldPath
fromFieldPath: "spec.engine"
toFieldPath: "spec.forProvider.engine"
- type: FromCompositeFieldPath
fromFieldPath: "spec.version"
toFieldPath: "spec.forProvider.engineVersion"
- name: subnet-group
base:
apiVersion: rds.aws.upbound.io/v1beta1
kind: SubnetGroup
spec:
forProvider:
region: us-east-1
description: "Managed by Crossplane"
Now a developer simply requests a database using the simplified API:
# my-database-claim.yaml
# A developer's request -- no cloud knowledge required
apiVersion: platform.example.com/v1alpha1
kind: DatabaseClaim
metadata:
name: orders-db
namespace: orders-team
spec:
size: medium
engine: postgres
version: "15"
3. How Crossplane Differs from Terraform
Both Crossplane and Terraform manage infrastructure as code, but they differ fundamentally in architecture and operational model:
| Aspect | Crossplane | Terraform |
|---|---|---|
| Execution Model | Continuous reconciliation (Kubernetes controller) | One-time apply (plan/apply cycle) |
| Drift Detection | Automatic and continuous | Only on terraform plan |
| State Management | Kubernetes etcd (distributed, HA) | Terraform state file (needs locking, remote backend) |
| API | Kubernetes API (kubectl, RBAC, admission webhooks) | Terraform CLI + HCL |
| Multi-tenancy | Native via Kubernetes namespaces and RBAC | Requires workspaces or separate state files |
| GitOps Integration | Native (it is just Kubernetes YAML) | Requires wrappers (Atlantis, Spacelift) |
| Learning Curve | Steep if new to CRDs and Compositions | Moderate (HCL is purpose-built) |
When to use Crossplane: When you want a self-service platform where developers provision infrastructure via Kubernetes, when you need continuous drift correction, or when your infrastructure and applications need to be managed through a single GitOps pipeline.
When to use Terraform: When you need to manage infrastructure that predates or exists outside of Kubernetes (networking, IAM, account setup), when your team already has deep Terraform expertise, or for initial cluster bootstrapping (you need Terraform to create the cluster that runs Crossplane).
Many organizations use both: Terraform for foundational infrastructure (VPCs, clusters, IAM) and Crossplane for application-level infrastructure (databases, caches, queues) that developers self-service.
4. Package Management
Crossplane uses a package system for distributing Providers and Configurations (bundles of XRDs and Compositions):
# Install a Configuration package that includes XRDs and Compositions
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: platform-ref-aws
spec:
package: xpkg.upbound.io/upbound/platform-ref-aws:v0.9.0
Configuration packages allow platform teams to version, test, and distribute their abstractions as OCI images, just like container images. This enables platform-as-a-product workflows where internal teams publish and consume standardized infrastructure APIs.
5. Provisioning a GCP CloudSQL Instance
Crossplane works across cloud providers. Here is an example provisioning a GCP CloudSQL instance using the GCP provider:
# cloudsql-instance.yaml
# Provision a GCP CloudSQL PostgreSQL instance
apiVersion: sql.gcp.upbound.io/v1beta1
kind: DatabaseInstance
metadata:
name: production-cloudsql
spec:
forProvider:
region: us-central1
databaseVersion: POSTGRES_15
settings:
- tier: db-custom-4-16384 # 4 vCPUs, 16GB RAM
diskSize: 100
diskType: PD_SSD
availabilityType: REGIONAL # High availability with automatic failover
backupConfiguration:
- enabled: true
startTime: "02:00" # Daily backups at 2 AM
pointInTimeRecoveryEnabled: true
ipConfiguration:
- ipv4Enabled: false # Disable public IP
privateNetworkRef:
name: my-vpc-connection
maintenanceWindow:
- day: 7 # Sunday
hour: 4 # 4 AM
writeConnectionSecretToRef:
name: cloudsql-connection
namespace: app-namespace
providerConfigRef:
name: gcp-provider-config
This demonstrates the power of Crossplane: the same workflow (write YAML, commit to Git, apply to cluster) works regardless of whether you are provisioning AWS RDS, GCP CloudSQL, or Azure Database. Platform teams can write Compositions that abstract away these differences entirely, allowing developers to request a "Database" without knowing which cloud it lands on.
6. GitOps with Crossplane
Because Crossplane resources are standard Kubernetes objects, they integrate naturally with ArgoCD or Flux:
- Platform team defines XRDs and Compositions in a Git repository.
- ArgoCD syncs these definitions to the management cluster.
- Developers create Claims (DatabaseClaim, CacheClaim) in their application repositories.
- ArgoCD syncs the Claims, Crossplane provisions the infrastructure.
- Connection details are written to Kubernetes Secrets, which applications reference directly.
This creates a fully declarative, auditable, self-service infrastructure platform where every change goes through a pull request.
Connection Detail Propagation
One of the most powerful features of the Crossplane + GitOps combination is automatic connection detail propagation. When Crossplane provisions a database, it writes the connection details (hostname, port, username, password) to a Kubernetes Secret. Your application pod can then reference that Secret directly in its environment variables or volume mounts, creating a fully automated provisioning-to-consumption pipeline without any manual credential passing.
Observing Resource Status
Crossplane provides detailed status information on every managed resource. You can use standard kubectl commands to observe provisioning progress:
# Check the status of all managed resources
kubectl get managed
# Trace a composite resource to see all child resources
crossplane beta trace database.platform.example.com/orders-db
# Check events for troubleshooting
kubectl describe instance.rds.aws.upbound.io/production-postgres
Common Pitfalls
- Starting with raw Managed Resources instead of Compositions: Exposing raw MRs to developers defeats the purpose of Crossplane. Always build abstractions with XRDs and Compositions so developers get a simplified, opinionated API.
- Ignoring the ProviderConfig: Each Provider needs credentials configured via a ProviderConfig. Misconfigured or overly-broad IAM permissions are a common source of security issues.
- Not handling deletion policies: By default, deleting a Managed Resource deletes the cloud resource. For production databases, set
deletionPolicy: Orphanto prevent accidental data loss. - Underestimating Composition complexity: Compositions with many patches and transforms can become hard to debug. Use
crossplane beta traceto understand resource relationships. - Ignoring resource readiness: Compositions can depend on resources being ready before creating dependent resources. Use
readinessChecksand connection detail propagation carefully.
Best Practices
- Build a thin abstraction layer: Define XRDs with opinionated defaults (encryption on, backups enabled, private networking) so developers cannot accidentally create insecure infrastructure.
- Use EnvironmentConfigs for per-environment values: Instead of hardcoding regions or VPC IDs in Compositions, use EnvironmentConfigs to inject environment-specific values.
- Version your XRDs: Treat your platform API like any other API. Use
v1alpha1,v1beta1, andv1to signal stability, and implement conversion webhooks when evolving schemas. - Test Compositions in CI: Use
crossplane beta renderto test Compositions locally before deploying them to a cluster. - Monitor Provider health: Watch for
ProviderRevisionhealth status and Provider pod logs. A crashlooping Provider means no infrastructure reconciliation. - Separate management and workload clusters: Run Crossplane in a dedicated management cluster, not in the same cluster that runs your applications.
What's Next?
- CRDs & Operators: Crossplane is built entirely on the CRD and operator pattern -- understand these fundamentals to work effectively with Crossplane.
- GitOps (ArgoCD): Learn how to manage Crossplane resources through GitOps for a fully declarative infrastructure pipeline.
- Security Policies: Use RBAC and admission policies to control who can provision which infrastructure resources via Crossplane.