Skip to main content

Upgrade from Crossplane

Already running Crossplane? In this guide, you'll rehearse an upgrade on a throwaway cluster: stand up Crossplane with one resource, export its state, import it into a new Upbound control plane, activate it, and watch it reconcile in the Upbound hub.

Why upgrade to Upbound

Upbound Crossplane (UXP) adds operational features useful for Crossplane operators. UXP gives you a secrets proxy, backup and restore, and control plane insights. This tutorial's upgrade path allows you to keep your existing compositions and managed resources.

Prerequisites:

Before you begin, make sure you have:

  • kind (for the disposable "before" cluster)
  • kubectl
  • helm
  • up CLI
  • An Upbound account

Step 1: Log in to Upbound

up login

Step 2: Stand up a throwaway Crossplane control plane

The setup script creates a kind cluster, installs Crossplane and the provider-nop provider, creates a single composite resource so there's something real to migrate.

Crossplane setup script
setup-crossplane.sh
#!/usr/bin/env bash
set -euo pipefail

for bin in kind helm kubectl; do
command -v "$bin" >/dev/null 2>&1 || { echo "error: '$bin' not found on PATH" >&2; exit 1; }
done

echo "==> Creating kind cluster 'crossplane-source'"
kind create cluster --name crossplane-source

echo "==> Installing Crossplane"
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system --create-namespace --wait

echo "==> Installing provider-nop and function-patch-and-transform"
kubectl apply -f - <<'EOF'
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-nop
spec:
package: xpkg.upbound.io/crossplane-contrib/provider-nop:v0.4.0
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-patch-and-transform
spec:
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.9.0
EOF
kubectl wait --for=condition=healthy provider/provider-nop --timeout=3m
kubectl wait --for=condition=healthy function/function-patch-and-transform --timeout=3m

echo "==> Defining a composite resource type"
kubectl apply -f - <<'EOF'
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xapps.example.upbound.io
spec:
group: example.upbound.io
names:
kind: XApp
plural: xapps
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: xapps.example.upbound.io
spec:
compositeTypeRef:
apiVersion: example.upbound.io/v1alpha1
kind: XApp
mode: Pipeline
pipeline:
- step: create-nop
functionRef:
name: function-patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
resources:
- name: nop
base:
apiVersion: nop.crossplane.io/v1alpha1
kind: NopResource
spec:
forProvider:
conditionAfter:
- conditionType: Ready
conditionStatus: "True"
time: 5s
EOF

# The XRD needs a moment to establish the XApp CRD before the XR is accepted.
kubectl wait --for=condition=established crd/xapps.example.upbound.io --timeout=2m

echo "==> Creating one composite resource"
kubectl apply -f - <<'EOF'
apiVersion: example.upbound.io/v1alpha1
kind: XApp
metadata:
name: sample-app
spec: {}
EOF

cat <<'EOF'

Crossplane is running on kind cluster 'crossplane-source' with one XApp
composite resource. This is your migration source.
EOF

Download and run it:

curl -fsSL "https://docs.upbound.io/manifests/getstarted/migration/setup-crossplane.sh" -o setup-crossplane.sh
bash setup-crossplane.sh

Confirm the composite resource reaches a ready state before you continue:

kubectl get xapp sample-app

Step 3: Export your control plane's state

Export the source cluster's Crossplane state into a single archive.

warning

Point --kubeconfig at the throwaway cluster, not whatever context happens to be active. Exporting the wrong cluster is the first place this goes sideways.

up controlplane migration export \
--kubeconfig ~/.kube/config \
--output crossplane-export.tar.gz

The command reports the types it found, the resources it exported, and the archive it wrote:

Exporting control plane state...
Found 4 resource types
Exported 6 resources
Wrote archive to crossplane-export.tar.gz

Step 4: Create your Upbound control plane

Create the destination control plane and switch your context to it.

up controlplane create <name>
up ctx "<org>/<space>/<group>/<name>"
warning

up ctx points your active context to the new control plane. Confirm you're targeting the destination before continuing, so the import lands where you expect.

Step 5: Import the archive

up controlplane migration import --input crossplane-export.tar.gz

Imported resources land paused by default, so nothing reconciles yet. This is deliberate: it gives you a chance to review before anything acts on external infrastructure.

Importing control plane state...
Imported 6 resources (paused)
Import complete

Step 6: Review before activating

Spot-check that the imported resources and claims look right:

kubectl get managed
kubectl get composite
warning

This step is the highest-stakes moment in the migration. Before you unpause, confirm you're pointed at the new control plane, not the source cluster. Activating on the wrong cluster leaves two control planes reconciling the same external resources at once.

Step 7: Activate

Remove the paused annotation to let the new control plane take over reconciliation:

kubectl annotate managed --all crossplane.io/paused-

The resources move to a synced and ready state as the new control plane reconciles them:

kubectl get managed

Step 8: See it in the Console

Open the Upbound Console, select your new control plane, and find the migrated sample-app resource.

Clean up

When you're done with this quickstart, tear down the resources created:

kind delete cluster --name crossplane-source
note

Your real source cluster isn't disposable the way this one is. When you migrate production, decommission the original cluster only after you've confirmed the new control plane is healthy and reconciling.

Next steps