Crossplane is a Kubernetes add-on that lets you manage cloud infrastructure like databases, networks, and storage as Kubernetes resources.

Here’s a Kubernetes cluster with Crossplane installed and the AWS provider configured.

apiVersion: v1
kind: Namespace
metadata:
  name: crossplane-system
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/upbound/provider-aws:v0.37.0
apiVersion: secret
apiVersion: v1
kind: Secret
metadata:
  name: aws-creds
  namespace: crossplane-system
type: Opaque
data:
  creds: <base64_encoded_aws_access_key_id_and_secret_access_key>
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: creds

This setup allows you to define AWS resources, such as an S3 bucket, directly within Kubernetes manifests:

apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: my-crossplane-bucket-12345
spec:
  forProvider:
    region: us-east-1
    acl: private

When you kubectl apply this manifest, Crossplane’s AWS provider will translate it into an actual S3 bucket creation request to AWS. It continuously reconciles the desired state (your manifest) with the actual state in AWS, ensuring that if the bucket is ever deleted or modified outside of Kubernetes, Crossplane will restore it to its defined state.

The core problem Crossplane solves is the impedance mismatch between declarative infrastructure as code (like Kubernetes YAML) and the imperative, often complex APIs of cloud providers. Instead of writing Terraform, CloudFormation, or using AWS SDKs directly, you manage all your infrastructure – cloud resources and application workloads – using the same Kubernetes API and tooling you’re already familiar with. This unifies your operational model, reduces context switching, and enables GitOps workflows for your entire stack.

Internally, Crossplane works by installing "providers" into your Kubernetes cluster. Each provider is a controller that understands a specific cloud or service’s API. For AWS, provider-aws knows how to talk to the AWS API. When you define a Bucket resource in Kubernetes, the provider-aws controller watches for these resources. Upon creation, it makes the corresponding API calls to AWS to provision the bucket. It then updates the status of your Kubernetes Bucket resource with the actual state from AWS (like the bucket’s ARN and endpoint). For updates or deletions, it again translates Kubernetes resource changes into AWS API calls.

The ProviderConfig resource is crucial. It tells the Crossplane provider how to authenticate with the cloud. In this example, we’re using a Kubernetes Secret named aws-creds in the crossplane-system namespace. This secret must contain your AWS access key ID and secret access key, base64 encoded. The creds key within the secret is where the provider looks for these credentials.

Here’s what that base64 encoded secret might look like if you were to create it manually:

echo -n '{"aws_access_key_id": "YOUR_ACCESS_KEY_ID", "aws_secret_access_key": "YOUR_SECRET_ACCESS_KEY"}' | base64

You would replace YOUR_ACCESS_KEY_ID and YOUR_SECRET_ACCESS_KEY with your actual AWS credentials.

The package field in the Provider resource points to a specific version of the Crossplane provider image. Using a specific version like v0.37.0 ensures that your infrastructure provisioning is repeatable and not subject to unexpected changes from newer, potentially incompatible provider versions. Crossplane will pull this image and run it as a deployment within the crossplane-system namespace.

The spec.forProvider block within the Bucket resource maps directly to the parameters accepted by the AWS S3 CreateBucket API call. Fields like region and acl are passed through to AWS. Crossplane handles the translation and communication.

A common point of confusion is how Crossplane manages the lifecycle of cloud resources. When you create a Bucket resource, Crossplane provisions it. If you then kubectl delete that Bucket resource, Crossplane will call the AWS API to delete the S3 bucket. This two-way reconciliation ensures that your Kubernetes manifests are always the single source of truth for your infrastructure.

The ProviderConfig’s secretRef.key field is often overlooked. If your AWS credentials are not stored under the key creds in your Kubernetes secret, the provider will fail to authenticate. You must ensure this key matches the data key in your Secret object.

The next step is to explore composite resources, which allow you to abstract away complex cloud infrastructure into simpler, reusable building blocks.

Want structured learning?

Take the full Crossplane course →