Kubernetes
10 min readJune 26, 2026Updated September 19, 2026

kro vs Crossplane vs Helm: Choosing How to Build Kubernetes Platform Abstractions

AJ
Ajeet Yadav
Platform & Cloud Engineer
kro vs Crossplane vs Helm: Choosing How to Build Kubernetes Platform Abstractions

Quick answer

Helm packages YAML, kro turns a graph of resources into a custom API, and Crossplane is a control plane for infrastructure. They're constantly compared, but they sit at different altitudes — here's what each actually does and how to choose.

10 min read · Kubernetes

If you're building an internal platform on Kubernetes, you eventually need to give developers something simpler than raw manifests — "give me a database," "give me a service with its ingress, secrets, and autoscaler." Three tools come up constantly for this job: Helm, Crossplane, and the newer kro (Kube Resource Orchestrator). They get lined up as competitors, but that framing is misleading. They operate at different altitudes:

  • Helm packages and templates Kubernetes YAML.
  • kro turns a graph of Kubernetes resources into a single custom API, with no controller code.
  • Crossplane is a control plane that provisions and continuously reconciles infrastructure — including external cloud resources — behind your own APIs.

This post explains how each one actually works, where they overlap, and a decision rule for picking between them.


How Helm Works

Helm is a package manager. A chart is a directory of Go-templated YAML plus a values.yaml of parameters. At install or upgrade time, Helm renders the templates into concrete manifests and applies them to the cluster, tracking the result as a release.

yaml
1# templates/deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5  name: {{ .Release.Name }}-api
6spec:
7  replicas: {{ .Values.replicas }}
8  template:
9    spec:
10      containers:
11        - name: api
12          image: "{{ .Values.image.repo }}:{{ .Values.image.tag }}"

That's Helm's whole model: string-template YAML, then apply it. Its strengths follow directly — it's the universal format for distributing third-party software (you helm install Prometheus, cert-manager, and yes, kro and Crossplane themselves), and values.yaml gives consumers a clean knob panel.

Its weaknesses follow just as directly. Templating is string manipulation of whitespace-sensitive YAML, with no type checking until the rendered output hits the API server. And Helm does not reconcile: once a release is applied, Helm doesn't run a control loop to correct drift. If someone edits the Deployment by hand, Helm won't know until your next helm upgrade. That's why Helm is so often paired with a GitOps controller like Argo CD or Flux — see Helm best practices for production and, for where Helm fits against plain overlays, Helm vs Kustomize.

How kro Works

kro is a Kubernetes SIG Cloud Provider subproject (originally a joint effort from AWS, Google Cloud, and Microsoft Azure). Instead of templating YAML, you author a ResourceGraphDefinition (RGD) — a blueprint that describes the custom API you want and the resources each instance should create. kro turns that RGD into a real CRD, watches for instances of it, and reconciles the underlying resources for each one.

yaml
1apiVersion: kro.run/v1alpha1
2kind: ResourceGraphDefinition
3metadata:
4  name: web-app
5spec:
6  schema:
7    apiVersion: v1alpha1
8    kind: WebApp
9    spec:
10      name: string
11      image: string
12      replicas: integer | default=2
13  resources:
14    - id: deployment
15      template:
16        apiVersion: apps/v1
17        kind: Deployment
18        metadata:
19          name: ${schema.spec.name}
20        spec:
21          replicas: ${schema.spec.replicas}
22          # ... uses ${schema.spec.image}
23    - id: service
24      template:
25        apiVersion: v1
26        kind: Service
27        metadata:
28          name: ${schema.spec.name}
29        spec:
30          selector:
31            app: ${deployment.metadata.name}   # reference creates a dependency

Two things make kro distinct:

  • CEL instead of text templating. Fields are referenced with CEL expressions (${schema.spec.replicas}, ${deployment.status...}). CEL is typed, always terminates, and has no side effects — so a whole class of "I produced invalid YAML with a bad {{ if }}" errors disappears.
  • The dependency graph is inferred, not declared. You never set sync waves or ordering. kro reads your CEL references, builds a DAG, and waits for a referenced field (including status values) to exist before wiring it into a dependent resource. Forward references "just work."

The result is a new first-class API — kubectl get webapp — that your developers use, with no custom controller written in Go. Because kro composes any Kubernetes resource, native or CRD, it can wrap cloud-provider CRDs (AWS ACK, Azure ASO, GCP Config Connector) into the same abstraction.

Maturity warning — read this before you bet a platform on it. kro is pre-1.0 (currently the v0.9.x / v1alpha1 line) and is not marked production-ready. The API can still change. It's a strong choice for evaluation, internal tooling, and greenfield platform work where you control the upgrade cadence — but pin versions, expect breaking changes, and don't treat it like a graduated CNCF project yet.

How Crossplane Works

Crossplane is a different beast: a control plane. Its providers extend the Kubernetes API with Managed Resources — Kubernetes objects that represent external infrastructure (an RDS instance, an S3 bucket, a GCP network). Crossplane's providers talk directly to the cloud APIs and continuously reconcile those resources, correcting drift the way Kubernetes reconciles a Deployment.

On top of Managed Resources you build your own platform APIs with CompositeResourceDefinitions (XRDs) and Compositions: the XRD defines the API your developers consume, and the Composition declares which underlying resources an instance produces.

Crossplane shipped a significant v2 that modernized this model, and it matters for an accurate comparison:

  • Composite Resources (XRs) are namespaced by default, aligning with normal Kubernetes conventions.
  • Claims are removed — with namespaced XRs there's no longer a need for the separate claim object (apiextensions.crossplane.io/v2 doesn't support claims; v1-style XRDs fall back to a LegacyCluster mode for backward compatibility).
  • Composition Functions are now the standard. The old embedded patch-and-transform style is gone; compositions are a pipeline of functions (which can even read existing cluster state to decide desired state).

This is the heaviest of the three — you run a control plane and provider pods — but it's also the only one that provisions and owns external infrastructure. If you want the full picture, see Crossplane: infrastructure as code on Kubernetes and building platform infrastructure with Crossplane.


Terraform Day-2 Operations Checklist

State hygiene, drift, imports, policy checks, and upgrade routines — everything after `terraform apply` works. Plain Markdown, commit it to your repo.

Free. Instant download. You'll also get the occasional deep-dive from the newsletter — unsubscribe anytime.

kro vs Crossplane vs Helm: The Real Differences

DimensionHelmkroCrossplane
What it isPackage manager / templatingCRD factory for composing K8s resourcesControl plane for infrastructure
MechanismGo-templated YAML → applyRGD → generated CRD + reconcilerProviders + XRDs + Compositions
Expression modelGo text templates (untyped strings)CEL (typed, terminating)Composition Functions (pipeline)
ReconciliationNo control loop (apply-time only)ContinuousContinuous, with drift correction
Dependency orderingManual (hooks, weights)Inferred from CEL references (DAG)Handled by functions / reconciliation
Provisions external cloud infra?No (only what you template)Only via cloud CRDs (ACK/ASO/Config Connector)Yes — providers call cloud APIs directly
MaturityCNCF graduated, ubiquitousAlpha (v0.9.x), not GAMature, function-based v2
Best atPackaging & distributing appsLightweight K8s-native platform APIsOwning infrastructure behind custom APIs

The cleanest way to hold the distinction:

  • Helm is a tarball of YAML with variables. It ships software.
  • kro is a CRD factory. It turns a graph of existing Kubernetes resources into one custom, reconciled API — without writing a controller.
  • Crossplane is a control plane. It manages the lifecycle of real infrastructure (cloud and Kubernetes) and lets you expose it through your own APIs.

Which Should You Use?

  1. Packaging an app for others to install (or installing third-party software)? Use Helm. Nothing else here replaces it as a distribution format — you'll likely install kro and Crossplane with Helm.
  2. Want developers to consume a simple custom API (kind: WebApp, kind: Tenant) that fans out into existing Kubernetes resources — and you want reconciliation without writing a controller? Use kro. It's the lightest path to a platform abstraction, as long as you accept its alpha status.
  3. Need to provision and continuously own external cloud infrastructure (databases, networks, queues) behind your own platform APIs, across many providers? Use Crossplane. The provider ecosystem and control-plane model are the point, and they're production-proven.
  4. Building a serious internal developer platform? You'll probably use more than one. A common shape: Helm installs the platform components; Crossplane provisions the cloud infrastructure; kro (or Crossplane Compositions) stitches resources into the developer-facing APIs. kro can even compose Crossplane's Managed Resources into a higher-level interface.

The honest tie-breaker between kro and Crossplane: if your building blocks are already Kubernetes resources (including cloud CRDs via ACK/ASO/Config Connector) and you want something light, kro is compelling. If you need to drive cloud APIs directly with a mature provider ecosystem and drift correction, Crossplane is the safer bet today — not least because it's GA and kro is not.

If you're standing up the platform layer this sits on, platform engineering golden paths and Terraform vs Pulumi cover the surrounding decisions.


Frequently Asked Questions

Is kro production-ready?

Not yet. kro is pre-1.0 — the current line is v0.9.x with a v1alpha1 API — and the project does not advertise production readiness. It's a good fit for evaluation, internal tooling, and greenfield platforms where you control upgrades, but you should pin versions and expect breaking API changes. Crossplane and Helm, by contrast, are both mature and widely run in production.

Can I use kro and Crossplane together?

Yes, and it's a natural pairing. Crossplane's Managed Resources are just Kubernetes objects, so kro can compose them — along with native resources and ACK/ASO/Config Connector CRDs — into a single higher-level API. Crossplane owns the infrastructure lifecycle; kro gives you a lighter, CEL-based way to package it into a developer-facing kind.

Does Helm reconcile drift like kro and Crossplane?

No. Helm renders templates and applies them at install or upgrade time; it does not run a control loop, so manual changes to a release go uncorrected until your next helm upgrade. kro and Crossplane both run continuous reconciliation. This is exactly why Helm is so often paired with a GitOps controller — see Argo CD App-of-Apps vs ApplicationSet for how teams add continuous sync on top of Helm.

Aren't kro and Crossplane Compositions the same thing?

They overlap — both build custom APIs from existing resources — but are not the same. Crossplane is a full control plane whose providers talk directly to cloud APIs, composed in v2 via Composition Functions. kro is lighter and Kubernetes-native: it composes resources that already exist as Kubernetes objects using CEL, with no providers of its own. Choose kro when the building blocks are already on-cluster; Crossplane for its provider ecosystem and drift correction.

Do I still need Helm if I adopt kro or Crossplane?

Almost certainly yes. Helm operates at a different altitude — packaging and distribution — and remains the standard way to install third-party software, including kro and Crossplane themselves. kro and Crossplane define your platform APIs; Helm ships software. They coexist rather than compete.


For the broader infrastructure-as-code picture, see Crossplane infrastructure as code on Kubernetes, Helm advanced patterns for production, and Terraform vs Pulumi.

Designing the abstraction layer for an internal developer platform? Talk to us at Coding Protocols — we help platform teams choose where templating ends and a control plane begins.

Official References

Was this article helpful?

Be the first to rate this article

Related Topics

kro
Crossplane
Helm
Platform Engineering
Kubernetes
Infrastructure as Code
GitOps

Found this useful? Share it.

Practice this

Related tools

Read Next