Skip to content
Alex Chiri's Blog
Go back

What is Crossplane and how it helps platform teams

A central control hub with concentric feedback rings, thin radial orbit lines reaching out to abstract cloud-resource shapes, and a simple rounded rectangle floating above it

I keep writing about platforms and platform teams, and one tool that comes up a lot these days in that world is Crossplane. If you don’t work deep in infrastructure, it can feel like yet another Kubernetes thing with strange names (“compositions”, “XRDs”, “providers”) and it is not always clear what problem it is actually trying to solve.

So this post is for the people who understand Kubernetes and cloud resources in principle, but are not living in them every day. I want to go over three things:

  1. What Crossplane is and how it works, in plain words.
  2. How you can use compositions to build abstractions and simplification layers on top of your infrastructure.
  3. How you can use Crossplane to simply manage cloud resources.

The problem Crossplane is trying to solve

If you run anything in the cloud, you know the usual story: there is a database in AWS, a storage bucket somewhere, a message queue, some network rules, maybe a Kubernetes cluster, and so on. Each of these things needs to be created, configured, updated and eventually deleted.

Over the years, we got a lot of tools to help with this: Terraform, Pulumi, CloudFormation, the cloud provider CLIs, ad-hoc scripts, and so on. Most of them are perfectly capable of managing multiple clouds and many kinds of resources, so this is not really a “one tool per cloud” problem. Crossplane is, in that sense, on par with the others.

The difference shows up when you put this next to how modern engineering teams actually run their applications. That part usually lives on Kubernetes: Deployments, Services, Helm charts, Argo CD or Flux, continuous reconciliation, RBAC, secrets, the whole model. So you end up with two worlds side by side:

These two worlds have different mental models, different permission systems, different tools for drift detection, and different ways of wiring things together. When a developer asks “how does my service get its database connection string?”, the answer often involves gluing the two worlds with scripts or CI jobs.

What Crossplane brings to the table is not “yet another multi-cloud tool” — it’s the idea of managing infrastructure using the same control plane and the same model you already use for your applications. One API, one reconciliation loop, one set of tools around it.

What Crossplane is

The shortest definition I can give is:

Crossplane is a control plane for your infrastructure that runs on top of Kubernetes.

Let’s unpack that.

Kubernetes is very good at one specific thing: you tell it what you want (a desired state), it keeps comparing that with reality, and if they don’t match, it changes reality until they do. That is what we call a control loop, and it is the heart of Kubernetes. Deployments, services, pods — all of them work like that.

Crossplane takes this idea and says: what if we used the same control loop to manage things that live outside the cluster? A database in AWS. A DNS zone. A GitHub repository. A user in some SaaS tool. Anything with an API.

To do that, Crossplane installs into your cluster and adds new types of Kubernetes resources that represent these external things. You then write YAML for, say, an RDSInstance, apply it like any other Kubernetes resource, and Crossplane talks to AWS on your behalf to make that database exist. If someone changes the database behind your back, the control loop will notice and reconcile it back. If you delete the Kubernetes resource, the database gets deleted too.

The pieces that make this possible are:

Using Crossplane to just manage cloud resources

Let’s start with the simpler of the two stories. Forget compositions for a moment. You can use Crossplane purely as a way to manage cloud resources from your cluster, instead of using Terraform or a CLI.

The flow looks like this:

  1. You install the Crossplane controller into your Kubernetes cluster (usually via a Helm chart).
  2. You install the provider you need, for example provider-aws-rds.
  3. You give Crossplane credentials to talk to AWS (via a ProviderConfig, usually pointing at a Kubernetes secret or an IAM role).
  4. You write a YAML file describing the database you want. Something like:
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
metadata:
  name: my-service-db
spec:
  forProvider:
    region: eu-west-1
    engine: postgres
    instanceClass: db.t3.small
    allocatedStorage: 20
    username: appuser
    autoGeneratePassword: true
  1. You kubectl apply it. Crossplane picks it up, creates the database in AWS, and keeps the Kubernetes object’s status in sync with what it observes in AWS.

That’s it. No state files to store somewhere. No pipelines to orchestrate plan and apply. The “state” is simply the Kubernetes resource, and the source of truth is the cluster.

Why is this nice, even on its own?

At this level, Crossplane is already pulling its weight: it replaces a good chunk of what you used to do with Terraform, and it plugs directly into the way your cluster already works. But the real magic is the next step.

Compositions: building abstractions for your users

This is the part that makes Crossplane interesting for platform teams.

If you expose raw managed resources to application developers, you are not really helping them. A developer who just wants “a Postgres database for my service” should not have to care about subnets, parameter groups, backup windows, encryption keys, and parameter groups (yes, I said it twice, because they really are annoying). Those are things the platform team should decide once, on behalf of the organisation.

Compositions are how you do that in Crossplane. The idea is:

Concretely, there are two things to write:

From a developer’s point of view, the experience becomes something like:

apiVersion: platform.mycompany.io/v1alpha1
kind: PostgresDatabase
metadata:
  name: payments-db
spec:
  size: small
  environment: prod
  team: payments

They apply this, and behind the scenes ten different managed resources get created with the right naming conventions, tags, network rules and backup policies — because the platform team baked those into the composition. If tomorrow the platform team wants to, say, change the default encryption key or add a new firewall rule to every database, they change the composition once, and every existing database gets reconciled.

This is where Crossplane really starts to look like a platform and not just a different flavour of IaC. You are not shipping infrastructure to your developers — you are shipping products that your developers consume, with a typed, validated API. The internal details become an implementation choice that can evolve over time without breaking the contract with users.

Why this combination matters

The two stories above are the same tool used at two different depths:

This combination — using the same tool for both the low-level plumbing and the high-level developer-facing API — is what makes Crossplane fit well with the platform engineering mindset. The boundary between “infra” and “product” stops being about which tool you use, and becomes about which layer of the composition you are looking at.

A few honest caveats

Before I wrap up, a few things worth being aware of before you adopt it:

Wrapping up

If I had to give one sentence that captures why I like Crossplane: it lets a platform team use one control plane for both “managing cloud resources” and “offering opinionated products to developers”, which is exactly the two jobs a platform team is usually trying to do at the same time.

If you are curious, a good first step is not to try to rewrite your entire infrastructure in Crossplane. Pick one resource — a bucket, a small database, a DNS record — and manage it with Crossplane for a while. Once that feels comfortable, wrap it in a small composition and see how the experience changes for the person consuming it. That’s usually when it clicks.


Share this post on:

Previous Post
A platform = a transformation
Next Post
Det var inte meningen