content/v2.0/guides/upgrade-to-crossplane-v2.md
Crossplane v2 introduces significant improvements while maintaining backward compatibility with most v1 configurations. This guide helps you upgrade from Crossplane v1.x to v2.
Learn about [new features in Crossplane v2]({{<ref "../whats-new">}}) including namespaced resources, the ability to compose any Kubernetes resource, and new operational workflows.
{{<hint "important">}}
Only upgrade to Crossplane v2 from Crossplane v1.20, the final v1.x release.
If you're running an earlier version, upgrade to v1.20 first.
{{</hint>}}
{{<hint "important" >}} Always upgrade Crossplane one minor version at a time, using the most recent patch version available for each.
For example, if you are on v1.19 and want to upgrade to v2.1, you should
first upgrade to v1.20, then v2.0, before finally upgrading to v2.1. The
upgrade path in this example looks like v1.19 → v1.20 → v2.0 → v2.1.
{{</hint>}}
{{<hint "note">}} There's no automated tooling yet to migrate existing v1 cluster-scoped XRs and MRs to v2 namespaced style. You can upgrade to Crossplane v2 and start using the new namespaced features right away - your existing v1 resources continue working unchanged. See crossplane/crossplane#6726 for migration tooling progress. {{</hint>}}
Before upgrading, ensure:
Crossplane v2 removes these deprecated features:
Deprecated in: v1.17 Replaced by: [Composition functions]({{<ref "../composition/compositions">}})
If you're using spec.mode: Resources in your Compositions, migrate to
composition functions before upgrading.
Migration help: use the Crossplane v1.20 CLI to automatically convert your Compositions:
# Convert patch and transform to function pipelines
crossplane beta convert pipeline-composition old-composition.yaml -o new-composition.yaml
Deprecated in: v1.11 Replaced by: [DeploymentRuntimeConfig]({{<ref "../packages/providers#runtime-configuration">}})
Update any ControllerConfig resources to use DeploymentRuntimeConfig instead.
Migration help: use the Crossplane v1.20 CLI to automatically convert your ControllerConfigs:
# Convert ControllerConfig to DeploymentRuntimeConfig
crossplane beta convert deployment-runtime controller-config.yaml -o deployment-runtime-config.yaml
Status: alpha feature, unmaintained
If you're using external secret stores, migrate to native Kubernetes secrets or External Secrets Operator before upgrading.
Removed: composite resources no longer have native connection details support.
You can recreate this feature by composing your own connection details Secret
as described in the [connection details composition guide]({{<ref "./connection-details-composition">}}).
Removed: --registry flag for default package registry
All packages must now use fully qualified names including the registry host name. Check your packages with:
kubectl get pkg -o wide
Update any packages without registry host names before upgrading. For example:
crossplane-contrib/provider-aws-s3:v1.23.0xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v1.23.0You can upgrade to Crossplane v2 if you meet these criteria:
If you're using any removed features, migrate away from them first.
The recommended upgrade approach:
Review your cluster for removed features and address any that you're using. Each removed feature section includes commands to inspect your cluster and migration tools to help convert resources.
Add the Crossplane Helm repository:
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
Upgrade to Crossplane v2:
helm upgrade crossplane \
--namespace crossplane-system \
crossplane-stable/crossplane
Verify the upgrade:
kubectl get pods -n crossplane-system
Crossplane v2 automatically creates a default [MRAP]({{<ref "../managed-resources/managed-resource-activation-policies">}}) that activates all managed
resources (*). Before installing v2 providers, you can optionally customize
this for better cluster resource efficiency.
Check what managed resources you use:
# See your managed resource types
kubectl get managed
Optionally, replace the default MRAP with a targeted one that activates only the resources you need:
# Delete the default catch-all MRAP
kubectl delete mrap default
Create a targeted MRAP:
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: my-resources
spec:
activate:
# Legacy cluster-scoped resources (existing v1 resources)
- buckets.s3.aws.upbound.io
- instances.ec2.aws.upbound.io
# Modern namespaced resources (new v2 resources)
- buckets.s3.aws.m.upbound.io
- instances.ec2.aws.m.upbound.io
{{<hint "tip">}}
Notice the distinction: s3.aws.upbound.io (legacy cluster-scoped) vs
s3.aws.m.upbound.io (v2 namespaced). The .m. indicates modern
namespaced managed resources.
{{</hint>}}
Upgrade your providers to versions that support both namespaced and cluster-scoped managed resources:
# Check current provider versions
kubectl get providers
Update your provider manifests to use v2 versions:
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: crossplane-contrib-provider-aws-s3
spec:
package: xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v2.0.0
{{<hint "note">}} Provider v2 releases support both legacy cluster-scoped and new namespaced managed resources. Your existing cluster-scoped MRs continue working unchanged. {{</hint>}}
After upgrading, you can begin using Crossplane v2 features:
Existing Compositions work with Crossplane v2 with minimal changes. v2 managed resources are schematically identical to v1 managed resources - they're just namespaced.
{{<hint "important">}} Don't update existing compositions that are actively used by composite resources in your control plane.
Updating a live composition that's in use by existing XRs could disrupt or replace your resources. Use the below migration approach only when creating new compositions for new resources. {{</hint>}}
To use v2 namespaced managed resources in compositions:
.crossplane.io to .m.crossplane.iov1beta1For example provider-aws-s3:v2.0.0 has two Bucket MRs:
apiVersion: s3.aws.upbound.io/v1beta2 - Legacy, cluster scopedapiVersion: s3.aws.m.upbound.io/v1beta1 - NamespacedThe spec.forProvider and status.atProvider fields are schematically
identical.
{{<hint "tip">}}
Use kubectl get mrds to see available MR API versions.
{{</hint>}}
{{<hint "note">}}
Not all providers use .crossplane.io domains. For example, provider-aws-s3
uses .upbound.io domains for historical reasons. The general pattern for
namespaced resources is adding .m to the existing domain: <domain> becomes
m.<domain> (like upbound.io → m.upbound.io or crossplane.io →
m.crossplane.io).
{{</hint>}}
Before (v1 cluster-scoped):
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: my-app
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: XBucket
mode: Pipeline
pipeline:
- step: create-bucket
functionRef:
name: crossplane-contrib-function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: s3.aws.upbound.io/v1beta2
kind: Bucket
metadata:
name: {{ .observed.composite.resource.metadata.name }}
spec:
forProvider:
region: us-east-2
After (v2 namespaced):
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: my-app
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: Bucket
mode: Pipeline
pipeline:
- step: create-bucket
functionRef:
name: crossplane-contrib-function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: s3.aws.m.upbound.io/v1beta1 # Added .m, reset to v1beta1
kind: Bucket
metadata:
name: {{ .observed.composite.resource.metadata.name }}
spec:
forProvider:
region: us-east-2
{{<hint "tip">}} Namespace handling in compositions:
metadata.namespace in templates.
Crossplane ignores template namespaces and uses the XR's namespace.scope: Cluster): Can compose resources in any
namespace. Include metadata.namespace in templates to specify the target
namespace.scope: LegacyCluster): Can't compose
namespaced resources.
{{</hint>}}Your existing v1 resources continue working in Crossplane v2:
These resources use LegacyCluster scope internally and maintain full
backward compatibility.
For example, existing v1-style XRDs continue working with claims:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xdatabases.example.crossplane.io
spec:
# v1 XRDs default to LegacyCluster scope (shown explicitly)
scope: LegacyCluster
group: example.crossplane.io
names:
kind: XDatabase
plural: xdatabases
claimNames:
kind: Database
plural: databases
# schema definition...
Users can create claims that work as before:
apiVersion: example.crossplane.io/v1
kind: Database
metadata:
name: my-database
namespace: production
spec:
engine: postgres
size: large
After upgrading:
Read more about [what's new in v2]({{<ref "../whats-new">}}) and explore the updated [composition documentation]({{<ref "../composition/compositions">}}).