Crossplane: Infrastructure as Code Using Kubernetes
Crossplane turns Kubernetes into a universal control plane for infrastructure. Learn how to provision AWS, GCP, and Azure resources as Kubernetes CRDs using Crossplane providers, compositions, and XRDs.
Crossplane: Infrastructure as Code Using Kubernetes
Crossplane turns your Kubernetes cluster into a universal infrastructure control plane. Instead of using separate tools for managing cloud resources (Terraform, Pulumi, CloudFormation), Crossplane lets you declare infrastructure resources as Kubernetes custom resources β and the Kubernetes reconciliation loop keeps them in the desired state.
The pitch: your platform team provides high-level abstractions (like a βDatabaseInstanceβ CRD), and application teams provision infrastructure by applying YAML to Kubernetes β the same workflow they already use for deploying applications.
Why Crossplane?
Before comparing to Terraform, understand Crossplaneβs position: itβs not competing with Terraform for all infrastructure-as-code use cases. It specifically targets platform teams building internal developer platforms on top of Kubernetes.
The value proposition:
- Single API surface: Application teams interact with one system (Kubernetes) for both application deployments and infrastructure provisioning
- Self-service with guardrails: Platform teams define allowed infrastructure configurations; application teams fill in parameters
- Continuous reconciliation: Crossplane watches for configuration drift and corrects it automatically, just like Kubernetes does for deployments
- GitOps-native: Infrastructure definitions are Kubernetes resources, so GitOps tools (ArgoCD, Flux) manage them naturally
- Composition: Platform teams can compose multiple cloud resources into higher-level abstractions
Architecture
Crossplane runs as a set of controllers in your Kubernetes cluster:
βββββββββββββββββββββββββββββββββββββββββββββββ
β Kubernetes Cluster β
β β
β ββββββββββββββββββββββββββββββββββββββββββββ
β β Crossplane Core ββ
β β Provider-AWS Provider-GCP ββ
β β Provider-Azure Provider-Helm ββ
β ββββββββββββββββ¬ββββββββββββββββββββββββββββ
β β Reconcile β
β ββββββββββββββββΌββββββββββββββββββββββββββββ
β β Cloud Providers ββ
β β AWS API GCP API Azure API ββ
β ββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββ
Providers are the adapters for cloud platforms. Provider-AWS manages AWS resources; Provider-GCP manages GCP; Provider-Helm manages Helm releases in your cluster.
Managed Resources (MRs) are direct mappings to cloud provider resources β an RDS instance, an S3 bucket, a GKE cluster.
Composite Resources (XRs) and Compositions are platform team abstractions that compose multiple managed resources.
Installation
# Install Crossplane
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm install crossplane --namespace crossplane-system \
--create-namespace crossplane-stable/crossplane
# Verify installation
kubectl get pods -n crossplane-system
Install the AWS provider:
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v1.1.0
Configure AWS credentials:
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: InjectedIdentity # Use IRSA or pod identity
For local development with static credentials:
# Create a credentials secret
kubectl create secret generic aws-credentials \
-n crossplane-system \
--from-file=credentials=./aws-credentials.txt
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-credentials
key: credentials
Managed Resources: Direct Cloud Resources
Managed Resources are 1:1 mappings to cloud provider APIs. Platform teams use these to provision infrastructure directly, or application teams use them when full control is needed.
Provision an S3 bucket:
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
name: my-data-bucket
annotations:
crossplane.io/external-name: acme-my-data-bucket-prod
spec:
forProvider:
region: us-east-1
tags:
Owner: data-team
Environment: production
providerConfigRef:
name: default
Provision an RDS instance:
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
metadata:
name: payments-db
spec:
forProvider:
region: us-east-1
instanceClass: db.t3.medium
engine: postgres
engineVersion: "15.4"
allocatedStorage: 100
storageType: gp3
dbName: payments
username: admin
passwordSecretRef:
namespace: crossplane-system
name: db-password
key: password
vpcSecurityGroupIds:
- sg-0123456789abcdef0
dbSubnetGroupName: private-subnets
backupRetentionPeriod: 7
deletionProtection: true
skipFinalSnapshot: false
finalSnapshotIdentifier: payments-db-final
tags:
Owner: payments-team
providerConfigRef:
name: default
Crossplane creates this RDS instance and monitors it. If someone manually modifies the instance in the AWS console, Crossplane detects the drift and corrects it.
Compositions: Higher-Level Abstractions
Compositions are where Crossplaneβs real power emerges. They let platform teams define higher-level abstractions from multiple managed resources.
Defining a Composite Resource (XRD)
First, define the schema for your abstraction:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xpostgresinstances.platform.acme.io
spec:
group: platform.acme.io
names:
kind: XPostgresInstance
plural: xpostgresinstances
claimNames:
kind: PostgresInstance
plural: postgresinstances
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
size:
type: string
enum: [small, medium, large]
description: "Database instance size"
storageGB:
type: integer
minimum: 20
maximum: 1000
region:
type: string
default: us-east-1
required:
- size
- storageGB
Defining the Composition
The composition maps the XRD parameters to actual managed resources:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: xpostgresinstances.aws.platform.acme.io
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: platform.acme.io/v1alpha1
kind: XPostgresInstance
resources:
- name: rds-instance
base:
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
spec:
forProvider:
region: us-east-1
engine: postgres
engineVersion: "15.4"
username: admin
backupRetentionPeriod: 7
deletionProtection: true
skipFinalSnapshot: false
providerConfigRef:
name: default
patches:
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.region
toFieldPath: spec.forProvider.region
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.storageGB
toFieldPath: spec.forProvider.allocatedStorage
- 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
- type: ToCompositeFieldPath
fromFieldPath: status.atProvider.endpoint
toFieldPath: status.endpoint
- name: rds-subnet-group
base:
apiVersion: rds.aws.upbound.io/v1beta1
kind: SubnetGroup
spec:
forProvider:
region: us-east-1
subnetIds:
- subnet-aaaa
- subnet-bbbb
providerConfigRef:
name: default
patches:
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.region
toFieldPath: spec.forProvider.region
Using the Abstraction
Application teams now provision databases using your high-level abstraction:
apiVersion: platform.acme.io/v1alpha1
kind: PostgresInstance # The claim type (namespaced)
metadata:
name: payments-db
namespace: payments-team
spec:
parameters:
size: medium
storageGB: 100
region: us-east-1
compositionSelector:
matchLabels:
provider: aws
writeConnectionSecretToRef:
name: payments-db-credentials
The application team has no visibility into the underlying RDS implementation β they just request a PostgresInstance and get back a connection secret. Platform teams control the allowed sizes, regions, and configurations.
Crossplane vs Terraform
The comparison is nuanced because the use cases overlap but arenβt identical:
| Dimension | Crossplane | Terraform |
|---|---|---|
| State storage | Kubernetes etcd | terraform.tfstate files / remote backend |
| Reconciliation | Continuous (like K8s) | Manual terraform apply |
| Drift detection | Automatic, continuous | Manual terraform plan |
| API | Kubernetes CRDs | HCL |
| Ecosystem | Growing (providers) | Mature (1000+ providers) |
| Self-service | Native (XRDs = abstraction layer) | Via Terraform modules + CI/CD |
| Non-K8s infrastructure | Limited | Full coverage |
| Learning curve | Higher (K8s knowledge required) | Lower (HCL is straightforward) |
Crossplane is better when: Youβre building a platform on Kubernetes and want application teams to provision infrastructure via Kubernetes APIs. The GitOps workflow is a first-class requirement.
Terraform is better when: You need to manage infrastructure beyond Kubernetes (VPCs, DNS, IAM, accounts), you have existing Terraform expertise, or your organization doesnβt run Kubernetes.
Many organizations use both: Terraform for foundational infrastructure (accounts, networks, Kubernetes clusters themselves), Crossplane for application-level infrastructure provisioned by teams within those clusters.
Real-World Pattern: Database-as-a-Service
A common Crossplane pattern: provide teams a self-service way to provision databases within approved guardrails.
Platform team provides:
XPostgresInstanceXRD with allowed sizes (small/medium/large)- Composition that creates the RDS instance with company-standard settings (encryption, backups, logging)
- RBAC that allows teams to create
PostgresInstanceclaims in their namespaces
Application team experience:
- Apply a
PostgresInstanceclaim YAML - Crossplane creates the RDS instance with platform-standard settings
- Crossplane writes the connection string to a Kubernetes secret
- The application references the secret β no manual credential handling
This pattern scales to any resource type: queues, caches, buckets, certificates, DNS records.
Observability
Crossplane exposes status conditions following the Kubernetes conditions pattern:
# Check a managed resource status
kubectl get postgresinstance.platform.acme.io payments-db -o yaml
# Status section shows:
status:
conditions:
- type: Ready
status: "True"
reason: Available
- type: Synced
status: "True"
reason: ReconcileSuccess
endpoint: payments-db.xxxxx.us-east-1.rds.amazonaws.com
Tools that understand Kubernetes conditions β Backstageβs Kubernetes plugin, Argo CD, monitoring systems β work with Crossplane resources naturally.
Getting Started Path
- Install Crossplane in a dev cluster
- Install Provider-AWS and configure credentials
- Provision a simple managed resource (an S3 bucket)
- Define your first composition for a common resource type (RDS, Redis, etc.)
- Expose the XRD to application teams
- Integrate with your GitOps tool (ArgoCD or Flux)
The learning curve is real β Crossplane requires solid Kubernetes knowledge and the composition syntax has edge cases. Budget time for the first composition, and it gets easier from there.
Conclusion
Crossplane earns its place in the platform engineering toolbox for teams building developer platforms on Kubernetes. The ability to define infrastructure abstractions as Kubernetes CRDs, reconcile continuously, and integrate natively with GitOps workflows makes it compelling for self-service infrastructure use cases.
For teams evaluating Crossplane vs Terraform: theyβre complementary more than competitive. Use Terraform for foundational infrastructure, Crossplane for the application-layer self-service infrastructure your developers provision day-to-day.
50 CLI Commands That Save Hours Every Week
The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.
- Git mastery (10 cmds)
- File & search wizardry
- Docker & DevOps tricks
- Process & network tools
- Productivity boosters
- macOS & Linux install guide
No spam. 1 email/week max. Unsubscribe anytime.
Found this guide useful? Check out our free developer tools.
Affiliate disclosure: Some links below are affiliate links β we may earn a small commission at no extra cost to you. Learn more.
Recommended Tools & Resources
DigitalOcean
$200 credit for new users. Simple, affordable cloud hosting for developers.
GitHub Student Pack
Free access to 100+ developer tools. Perfect for students and new devs.
Vercel
Deploy frontend apps instantly. Free tier is generous for side projects.
DevPlaybook Products
Boilerplates, scripts & AI toolkits to 10x your dev workflow.