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)kubectlhelm- 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
#!/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.
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>"
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
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
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
- Migrate a production control plane when you're ready for the real thing.
- Hub overview for the full-fleet story.