---
title: "Kubernetes Operators and Crossplane: Extending the Platform"
description: "Understand the operator pattern, common operators like cert-manager and Strimzi, and use Crossplane to provision cloud infrastructure as Kubernetes resources."
url: https://agent-zone.ai/knowledge/kubernetes/crossplane-and-operators/
section: knowledge
date: 2026-02-22
categories: ["kubernetes"]
tags: ["operators","crossplane","cert-manager","crds","infrastructure-as-code"]
skills: ["operator-usage","crossplane-composition","crd-management","infrastructure-provisioning"]
tools: ["kubectl","helm","crossplane-cli"]
levels: ["intermediate"]
word_count: 633
formats:
  json: https://agent-zone.ai/knowledge/kubernetes/crossplane-and-operators/index.json
  html: https://agent-zone.ai/knowledge/kubernetes/crossplane-and-operators/?format=html
  api: https://api.agent-zone.ai/api/v1/knowledge/search?q=Kubernetes+Operators+and+Crossplane%3A+Extending+the+Platform
---


# Kubernetes Operators and Crossplane

## The Operator Pattern

An operator is a CRD (Custom Resource Definition) paired with a controller. The CRD defines a new resource type (like `Certificate` or `KafkaCluster`). The controller watches for instances of that CRD and reconciles actual state to match desired state. This is the same reconciliation loop that powers Deployments, extended to anything.

Operators encode operational knowledge into software. Instead of a runbook with 47 steps to create a Kafka cluster, you declare what you want and the operator handles creation, scaling, upgrades, and failure recovery.

## Common Operators

### cert-manager

Automates TLS certificate lifecycle -- requests from Let's Encrypt, stores as Kubernetes Secrets, renews before expiry.

```bash
helm repo add jetstack https://charts.jetstack.io
helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager --create-namespace \
  --set crds.enabled=true
```

Create a ClusterIssuer and annotate your Ingress:

```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ops@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: payments-api
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - payments.example.com
    secretName: payments-tls
  rules:
  - host: payments.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: payments-api
            port:
              number: 8080
```

### external-dns

Syncs Kubernetes Ingress and Service resources to DNS providers (Route53, CloudFlare, Google Cloud DNS) automatically.

```bash
helm upgrade --install external-dns bitnami/external-dns \
  --namespace external-dns --create-namespace \
  --set provider=aws \
  --set domainFilters[0]=example.com
```

### Strimzi (Apache Kafka)

Manages Kafka clusters, topics, and users as Kubernetes resources.

```yaml
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
  name: payments-kafka
  namespace: kafka
spec:
  kafka:
    replicas: 3
    listeners:
    - name: plain
      port: 9092
      type: internal
      tls: false
    storage:
      type: persistent-claim
      size: 50Gi
  zookeeper:
    replicas: 3
    storage:
      type: persistent-claim
      size: 10Gi
```

Apply this and Strimzi creates StatefulSets, Services, PVCs, ConfigMaps, and handles rolling upgrades on spec changes.

## Crossplane: Infrastructure as Kubernetes Resources

Crossplane lets you provision cloud infrastructure (RDS, S3, VPCs) using `kubectl apply`. Platform teams define abstractions; application teams consume them through Claims.

### Installation

```bash
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm upgrade --install crossplane crossplane-stable/crossplane \
  --namespace crossplane-system --create-namespace
```

### Providers

```yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/upbound/provider-family-aws:v1.14.0
---
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: credentials
```

### Compositions, XRDs, and Claims

**XRD** defines your platform API. **Composition** maps it to cloud resources. **Claim** is what developers use.

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xdatabases.platform.example.com
spec:
  group: platform.example.com
  names:
    kind: XDatabase
    plural: xdatabases
  claimNames:
    kind: Database
    plural: databases
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              size:
                type: string
                enum: ["small", "medium", "large"]
              engine:
                type: string
                enum: ["postgres", "mysql"]
            required: ["size", "engine"]
---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: database-aws
spec:
  compositeTypeRef:
    apiVersion: platform.example.com/v1alpha1
    kind: XDatabase
  resources:
  - name: rds-instance
    base:
      apiVersion: rds.aws.upbound.io/v1beta2
      kind: Instance
      spec:
        forProvider:
          region: us-east-1
          engine: postgres
          skipFinalSnapshot: true
        providerConfigRef:
          name: default
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: "spec.engine"
      toFieldPath: "spec.forProvider.engine"
    - 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
```

A developer applies this Claim and gets a managed database without knowing about RDS or instance classes:

```yaml
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
  name: payments-db
  namespace: payments
spec:
  size: medium
  engine: postgres
```

## When to Use What

**Operators** -- Stateful applications needing active lifecycle management (databases, message queues). They encode operational expertise.

**Helm** -- Deploying applications with configurable defaults. Package manager, not a controller. No reconciliation after install.

**Terraform** -- Infrastructure provisioning with mature state management and plan/apply workflow. Operates outside the cluster.

**Crossplane** -- Self-service infrastructure abstractions consumed through `kubectl`. Everything stays in the Kubernetes API. Best when your team is Kubernetes-native.

The practical divide: Helm for apps in the cluster, operators for stateful workloads inside the cluster, Crossplane or Terraform for infrastructure outside it. Crossplane wins when you want a unified Kubernetes control plane. Terraform wins when you need broad ecosystem support and your team already knows HCL.

