1. The Concept of Abstraction in Crossplane#
To build an Internal Developer Platform (IDP), we must split our users into two personas:
- The Platform Engineer: Understands AWS. Writes complex configurations. Defines the company’s internal APIs.
- The Application Developer: Knows nothing about AWS subnets. Just wants a database to store user data.
The Platform Engineer’s job is to build a simple API for the Application Developer.
In Crossplane, we define this API by writing a Composite Resource Definition (XRD). This is a special Crossplane object that tells the Kubernetes API server: “Hey, I am creating a brand new API called XPostgreSQL. If anyone asks for one, here are the 3 simple arguments they are allowed to provide.”
2. Authoring an XRD#
Let’s author an XRD for a hypothetical company called AcmeCorp. We want to allow our developers to request a PostgreSQL database by simply specifying the size of the storage (in GB) and the environment type (dev or prod).
Create a file named xrd-postgres.yaml:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
# The name MUST follow the format: <plural>.<group>
name: xpostgresqlinstances.database.acmecorp.com
spec:
# 1. Define the API Group and Names
group: database.acmecorp.com
names:
kind: XPostgreSQLInstance
plural: xpostgresqlinstances
# 2. Define the exact API Schema we want to expose to our developers!
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
# Developers can specify the size
storageGB:
type: integer
description: "The size of the database in Gigabytes"
# Developers can specify the environment
environment:
type: string
enum: ["dev", "prod"]
description: "The target environment for this database"
# Make both parameters mandatory
required:
- storageGB
- environmentApplying the XRD#
Apply this to your cluster:
kubectl apply -f xrd-postgres.yamlIf the command succeeds, Crossplane will dynamically compile this OpenAPI schema and inject it into the Kubernetes API server.
You can verify that your brand new API exists by checking the cluster’s CRDs:
kubectl get crds | grep acmecorpYou should see xpostgresqlinstances.database.acmecorp.com listed! You have successfully extended the Kubernetes API.
3. The Difference between XR and XRC#
Crossplane has a very strict security boundary regarding Kubernetes Namespaces.
- Cluster-Scoped (XR): The API we just created (
XPostgreSQLInstance) is Cluster-Scoped. This means it is meant for Cluster Administrators. If an application developer only has RBAC permissions to deploy to their specificnamespace, they will be denied permission to create anXPostgreSQLInstance. - Namespace-Scoped (XRC): To allow application developers to provision infrastructure, we must tell Crossplane to generate a Namespace-Scoped “Claim”.
Enabling Claims (XRCs)#
To generate a Claim, we add a claimNames block to our XRD. Modify your xrd-postgres.yaml:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xpostgresqlinstances.database.acmecorp.com
spec:
group: database.acmecorp.com
names:
kind: XPostgreSQLInstance
plural: xpostgresqlinstances
# Add this block!
claimNames:
kind: PostgreSQLInstance # Notice there is no 'X' prefix!
plural: postgresqlinstances
versions: # ... (schema remains exactly the same)Apply the updated file:
kubectl apply -f xrd-postgres.yamlCrossplane will now dynamically generate a second, namespace-scoped CRD called PostgreSQLInstance.
Application developers can now safely create PostgreSQLInstance claims in their own isolated namespaces. Crossplane will automatically watch for these claims, and silently create the cluster-scoped XPostgreSQLInstance (XR) behind the scenes to fulfill it.
4. Consuming the API (The Developer Experience)#
Now that the API is installed, let’s switch personas. We are now the Application Developer.
We don’t need to know anything about XRDs or AWS. We just look at the internal wiki our Platform Team wrote, and we create a simple YAML file alongside our application code.
Create my-database-claim.yaml:
apiVersion: database.acmecorp.com/v1alpha1
kind: PostgreSQLInstance
metadata:
name: app-db
# I can deploy this safely into my isolated namespace!
namespace: default
spec:
parameters:
storageGB: 50
environment: devApply it:
kubectl apply -f my-database-claim.yamlIf you try to put environment: testing, Kubernetes will reject the YAML instantly because of the enum validation we wrote in the OpenAPI schema!
Troubleshooting & Common Errors#
kubectl applyrejects the Claim withValidationError- Root Cause: The Application Developer provided a value in their Claim (e.g.,
storageGB: "50"as a string) that violates thetype: integerOpenAPI schema defined in the XRD. - Solution: Fix the Claim YAML to match the exact data types specified in the XRD.
- Root Cause: The Application Developer provided a value in their Claim (e.g.,
The XR is created, but no AWS resources are provisioning
- Root Cause: The XRD only defines the API. It does not define the implementation. Right now, Crossplane accepts the Claim, but it has no instructions on what physical AWS resources to create when it receives a
PostgreSQLInstance. - Solution: We must write a Composition. (See Episode 5).
- Root Cause: The XRD only defines the API. It does not define the implementation. Right now, Crossplane accepts the Claim, but it has no instructions on what physical AWS resources to create when it receives a
Conclusion & Next Steps#
You have successfully designed a custom Kubernetes API that hides all cloud complexity from the end user. You have established a clean boundary between Platform Engineering (XRDs) and Application Development (Claims).
However, as we discovered in the troubleshooting section, our API is currently a “mock”. It accepts requests, but it doesn’t actually spin up any RDS databases in AWS yet.
In Episode 5: Compositions, we will build the Translation Engine. We will tell Crossplane exactly how to map the simple storageGB integer from our XR into complex, multi-resource AWS topologies.

