Add documentation for MRDs, MRAPs, and safe-start

Large Crossplane providers install hundreds of CRDs consuming significant API
server resources, even when users only need a few resource types. The new
alpha ManagedResourceDefinition (MRD) and ManagedResourceActivationPolicy
(MRAP) features in Crossplane v2.0+ solve this by enabling selective
activation of provider resources, but lack user-facing documentation.

This change adds complete documentation covering both concepts and practical
implementation. The MRD concepts page explains the CRD scaling problem and
selective activation approach. The MRAP concepts page details pattern-based
activation strategies and multiple policy coordination. Two how-to guides
provide end-to-end workflows: one for users wanting to reduce CRD overhead
through selective activation, and another for provider developers implementing
safe-start capability. The user guide was tested with simulated MRDs to verify
the activation workflow and troubleshooting steps.

Signed-off-by: Nic Cope <nicc@rk0n.org>
This commit is contained in:
Nic Cope 2025-08-08 16:29:16 -07:00
parent d4d3429c7a
commit 8dfa231a1d
10 changed files with 1301 additions and 1 deletions

View File

@ -0,0 +1,214 @@
---
title: Disabling Unused Managed Resources
weight: 85
state: alpha
alphaVersion: 2.0
description: Reduce CRD overhead by disabling unused managed resources
---
{{<hint "important">}}
This guide uses
[managed resource definitions]({{<ref "../managed-resources/managed-resource-definitions">}})
and
[managed resource activation policies]({{<ref "../managed-resources/managed-resource-activation-policies">}}),
which Crossplane v2.0+ enables by default. To disable this behavior, set
`--enable-custom-to-managed-resource-conversion=false` when installing
Crossplane.
{{</hint>}}
Large Crossplane providers can install 100+ managed resource CRDs, consuming
significant cluster resources even when you only need one or two resource
types. This guide shows how to use
[ManagedResourceDefinitions]({{<ref "../managed-resources/managed-resource-definitions">}})
and
[ManagedResourceActivationPolicies]({{<ref "../managed-resources/managed-resource-activation-policies">}})
to install only the provider resources you actually need.
## Before you begin
This guide requires:
- Crossplane v2.0+ installed in your cluster
- A provider with `safe-start` capability (this guide uses
`provider-aws-ec2:v2.0.0`)
- Basic familiarity with Kubernetes and Crossplane concepts
{{<hint "important">}}
ManagedResourceDefinitions and ManagedResourceActivationPolicies are alpha
features in Crossplane v2.0+.
{{</hint>}}
## The problem: Resource overhead
Installing a large cloud provider in Crossplane creates hundreds of CRDs:
```shell
# Before selective activation - provider-aws-ec2 installs ~200 CRDs
kubectl get crds | grep aws.crossplane.io | wc -l
# Output: 200
# Each CRD consumes ~3 MiB of API server memory
# 200 CRDs × 3 MiB = 600 MiB of memory usage
```
Most users only need a small subset of these resources. Selective activation
lets you install just what you need.
## Step 1: Disable automatic activation
By default, the Crossplane Helm chart creates an activation policy that
enables all provider resources. To use selective activation, disable this
default behavior.
<!-- vale Google.Headings = NO -->
### Option A: Helm installation
<!-- vale Google.Headings = YES -->
```shell
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--create-namespace \
--set provider.defaultActivations={}
```
<!-- vale Google.Headings = NO -->
### Option B: Existing installation
<!-- vale Google.Headings = YES -->
Delete the default activation policy:
```shell
kubectl delete managedresourceactivationpolicy default
```
## Step 2: Install your provider
Install your provider as normal. Crossplane automatically converts the
provider's CRDs to ManagedResourceDefinitions:
```yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2
spec:
package: xpkg.crossplane.io/provider-aws-ec2:v2.0.0
```
Save this as `provider.yaml` and apply it:
```shell
kubectl apply -f provider.yaml
# Wait for provider to be ready
kubectl wait --for condition=Healthy provider/provider-aws-ec2 --timeout=5m
```
## Step 3: Verify Crossplane created MRDs
<!-- vale Google.WordList = NO -->
After the provider installs, check ManagedResourceDefinitions that Crossplane
created in inactive state:
<!-- vale Google.WordList = YES -->
```shell
# List ManagedResourceDefinitions
kubectl get managedresourcedefinitions
# Check their states (should be "Inactive")
kubectl get mrds -o jsonpath='{.items[*].spec.state}' \
| tr ' ' '\n' | sort | uniq -c
# 200 Inactive
```
Notice that Crossplane didn't create any CRDs yet:
```shell
kubectl get crds | grep ec2.aws.crossplane.io
# No output - CRDs don't exist until MRDs are activated
```
## Step 4: Create an activation policy
Create a ManagedResourceActivationPolicy to selectively activate only the
resources you need:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: my-app-resources
spec:
activate:
- instances.ec2.aws.crossplane.io # EC2 instances for compute
- securitygroups.ec2.aws.crossplane.io # Security groups for networking
- vpcs.ec2.aws.crossplane.io # VPCs for isolation
```
Save this as `activation-policy.yaml` and apply it:
```shell
kubectl apply -f activation-policy.yaml
```
## Step 5: Verify selective activation
<!-- vale Google.WordList = NO -->
Check that Crossplane activated only the specified resources:
<!-- vale Google.WordList = YES -->
```shell
# Check MRD states - only some should be Active now
kubectl get mrds \
-o jsonpath='{range .items[*]}{.metadata.name}: {.spec.state}{"\n"}{end}' \
| grep Active
# instances.ec2.aws.crossplane.io: Active
# securitygroups.ec2.aws.crossplane.io: Active
# vpcs.ec2.aws.crossplane.io: Active
# Verify Crossplane created corresponding CRDs
kubectl get crds | grep ec2.aws.crossplane.io
# instances.ec2.aws.crossplane.io
# securitygroups.ec2.aws.crossplane.io
# vpcs.ec2.aws.crossplane.io
# Count CRDs from EC2 provider - should match activated MRDs
kubectl get crds | grep ec2.aws.crossplane.io | wc -l
# 3 (only the activated resources)
```
## Step 6: Measure the impact
Check the significant reduction in resource overhead:
```shell
# Count CRDs from EC2 provider - should be much lower than 200
kubectl get crds | grep aws.crossplane.io | wc -l
# 3 CRDs (99% reduction from 200)
# Calculate memory savings
echo "197 CRDs saved × 3 MiB = 591 MiB saved (99% reduction)"
# Verify inactive MRDs still exist but consume minimal resources
kubectl get mrds \
-o jsonpath='{.items[?(@.spec.state=="Inactive")]..metadata.name}' | wc -w
# 197 inactive MRDs (~20 MiB total overhead vs 600 MiB for active CRDs)
# Check total MRDs (active + inactive)
kubectl get mrds | wc -l
# 200 total MRDs (3 active, 197 inactive)
```
The selective activation provides massive resource savings while maintaining
full capability for the resources you actually use.
## Next steps
- Learn more about
[ManagedResourceDefinitions]({{<ref "../managed-resources/managed-resource-definitions">}})
for detailed concepts and troubleshooting
- Explore
[ManagedResourceActivationPolicies]({{<ref "../managed-resources/managed-resource-activation-policies">}})
for advanced activation strategies and best practices
- Check the [API reference]({{<ref "../api">}}) for complete schema
documentation

View File

@ -0,0 +1,247 @@
---
title: Implementing safe-start in Providers
weight: 90
state: alpha
alphaVersion: 2.0
description: Guide for provider developers to add safe-start capability for
selective resource activation
---
This guide shows provider developers how to implement safe-start capability in
their Crossplane providers. safe-start enables
[disabling unused managed resources]({{<ref "disabling-unused-managed-resources">}})
through ManagedResourceDefinitions, improving performance and reducing resource
overhead.
{{<hint "important">}}
safe-start requires Crossplane v2.0+ and crossplane-runtime v2.0+.
Implementing safe-start involves code changes that affect provider startup
behavior.
{{</hint>}}
## What safe-start provides
safe-start changes how your provider handles CRD installation:
**Without safe-start:**
- Providers create all managed resource CRDs when installed
- Users get all resources even if they only need one or two
- Higher memory usage and API server load
**With safe-start:**
- Providers create ManagedResourceDefinitions but CRDs only when activated
- Users activate only needed resources through ManagedResourceActivationPolicies
- Significant reduction in cluster resource overhead
## Prerequisites
Before implementing safe-start:
- Provider built with crossplane-runtime v2.0+
- Understanding of
[ManagedResourceDefinitions]({{<ref "../managed-resources/managed-resource-definitions">}})
- Test environment with Crossplane v2.0+
## Implementation steps
### Step 1: Declare safe-start capability
Add safe-start to your provider package metadata:
```yaml
# package/crossplane.yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-example
spec:
capabilities:
- safe-start
```
### Step 2: Add required imports
Update your main.go imports (see
[crossplane-runtime godoc](https://pkg.go.dev/github.com/crossplane/crossplane-runtime/v2)
for full API reference):
```go
import (
// existing imports...
"k8s.io/apimachinery/pkg/runtime/schema"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"github.com/crossplane/crossplane-runtime/v2/pkg/controller"
"github.com/crossplane/crossplane-runtime/v2/pkg/gate"
"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/customresourcesgate"
)
```
### Step 3: Initialize the gate
Add gate initialization in your main function:
```go
func main() {
// existing setup code...
o := controller.Options{
// existing options...
Gate: new(gate.Gate[schema.GroupVersionKind]),
}
// Add CustomResourceDefinition to scheme for gate controller
if err := apiextensionsv1.AddToScheme(mgr.GetScheme()); err != nil {
panic(err)
}
// Setup controllers
if err := yourprovider.Setup(mgr, o); err != nil {
panic(err)
}
// Setup the CRD gate controller
if err := customresourcesgate.Setup(mgr, o); err != nil {
panic(err)
}
// start manager...
}
```
### Step 4: Use gated controller setup
Create a gated setup function for each managed resource controller:
```go
// SetupGated registers controller setup with the gate, waiting for the
// required CRD
func SetupGated(mgr ctrl.Manager, o controller.Options) error {
o.Gate.Register(func() {
if err := Setup(mgr, o); err != nil {
panic(err)
}
}, v1alpha1.MyResourceGroupVersionKind)
return nil
}
// Setup is your existing controller setup function (unchanged)
func Setup(mgr ctrl.Manager, o controller.Options) error {
// existing controller setup code...
}
```
### Step 5: Update controller registration
Change your controller setup to use the gated versions:
```go
// internal/controller/controller.go
func Setup(mgr ctrl.Manager, o controller.Options) error {
for _, setup := range []func(ctrl.Manager, controller.Options) error{
myresource.SetupGated, // Changed from myresource.Setup
// other gated setups...
} {
if err := setup(mgr, o); err != nil {
return err
}
}
return nil
}
```
## Implementation details
The safe-start implementation uses a "gate" pattern:
1. **Gate initialization**: Creates a gate that tracks CRD readiness
2. **Controller registration**: Controllers register with the gate, specifying
which CRDs they need
3. **CRD monitoring**: The `customresourcesgate` controller watches for CRD
creation/deletion
4. **Delayed startup**: Controllers only start when their required CRDs
become active
## Testing your implementation
Test safe-start behavior with this basic workflow:
```shell
# Install Crossplane v2.0+
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--set provider.defaultActivations={}
# Install your provider
kubectl apply -f provider.yaml
# Check that MRDs are created but inactive
kubectl get mrds
# All should show STATE: Inactive
# No CRDs should exist yet
kubectl get crds | grep yourprovider.io
# Should return no results
# Create activation policy
kubectl apply -f - <<EOF
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: test-activation
spec:
activate:
- "myresource.yourprovider.io"
EOF
# Verify activation worked
kubectl get mrd myresource.yourprovider.io
# Should show STATE: Active
# CRD should now exist
kubectl get crd myresource.yourprovider.io
```
## Troubleshooting
### Controllers never start
**Cause**: gate waits for CRDs that never become active.
<!-- vale Google.WordList = NO -->
**Solution**: check that Crossplane activated MRDs and created CRDs:
<!-- vale Google.WordList = YES -->
```shell
kubectl get mrds -o wide
kubectl describe mrap <activation-policy-name>
```
<!-- vale Google.Headings = NO -->
### CRDs don't appear
<!-- vale Google.Headings = YES -->
<!-- vale Google.Colons = NO -->
**Cause**: MRDs might not activate or activation policy doesn't match.
<!-- vale Google.Colons = YES -->
**Solution**: verify activation policy patterns match MRD names:
```shell
kubectl get mrds
kubectl get mrap -o yaml
```
## Migration considerations
When adding safe-start to existing providers:
- **Existing installations**: Continue working as expected (no CRD changes)
- **New installations**: Start with inactive MRDs, require activation policies
## Next steps
- Test your safe-start implementation with different activation patterns
- Update provider documentation to explain activation requirements
- Consider the user experience for providers that now require activation
policies
Learn more about the user experience in
[disabling unused managed resources]({{<ref "disabling-unused-managed-resources">}}).

View File

@ -1,5 +1,5 @@
--- ---
title: Managed Resources title: Managed Resources
weight: 52 weight: 52
description: Understand Crossplane's core components description: Understand Crossplane's managed resources and selective activation
--- ---

View File

@ -0,0 +1,430 @@
---
title: Managed Resource Activation Policies
weight: 20
state: alpha
alphaVersion: 2.0
description: ManagedResourceActivationPolicies control which
ManagedResourceDefinitions activate for selective provider resource
installation
---
{{<hint "important">}}
Managed resource activation policies work with
[managed resource definitions]({{<ref "managed-resource-definitions">}}),
which Crossplane v2.0+ enables by default. To disable this behavior, set
`--enable-custom-to-managed-resource-conversion=false` when installing
Crossplane.
{{</hint>}}
A `ManagedResourceActivationPolicy` (MRAP) controls which
[ManagedResourceDefinitions]({{<ref "managed-resource-definitions">}})
become active in your cluster. MRAPs enable selective installation of provider
resources, allowing you to activate only the 10 managed resources you need
instead of the 100+ that a provider ships.
## The selective activation problem
Modern Crossplane providers can ship dozens or hundreds of managed resources,
but most users only need a small subset. Before MRAPs, you got "all or
nothing" - installing a provider meant getting every managed resource it
supported, consuming unnecessary cluster resources.
MRAPs solve this by providing pattern-based activation of
ManagedResourceDefinitions, letting you choose which provider resources to
enable.
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## How MRAPs work
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
MRAPs contain activation patterns that match ManagedResourceDefinition names.
When you create or update an MRAP, Crossplane:
1. **Lists all MRDs** in the cluster
2. **Matches MRD names** against the activation patterns
3. **Activates matching MRDs** by setting their `state` to `Active`
4. **Updates the MRAP status** with the list of activated resources
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-core-resources
spec:
activate:
- buckets.s3.aws.crossplane.io
- instances.rds.aws.crossplane.io
- *.ec2.aws.crossplane.io # All EC2 resources
```
When you apply this MRAP, Crossplane activates the specified S3 Bucket, RDS
Instance, and all EC2 resources, leaving other AWS resources inactive.
## Key features
- **Pattern-based matching**: Use wildcards to activate groups of resources
- **Multiple policy support**: Different MRAPs can activate different resource
sets
- **Status tracking**: See which resources each policy activated
- **Automatic activation**: New MRDs matching existing patterns activate
automatically
## Pattern matching
### Exact matching
Specify complete MRD names for precise control:
```yaml
spec:
activate:
- buckets.s3.aws.crossplane.io
- databases.rds.aws.crossplane.io
- clusters.eks.aws.crossplane.io
```
### Wildcard patterns
Use `*` wildcards to match multiple resources:
```yaml
spec:
activate:
- "*.s3.aws.crossplane.io" # All S3 resources
- "*.ec2.aws.crossplane.io" # All EC2 resources
- "*.rds.aws.crossplane.io" # All RDS databases
```
{{<hint "important">}}
MRAPs use prefix-only wildcards, not full regular expressions. Only `*` at
the beginning of a pattern works (for example, `*.s3.aws.crossplane.io`).
Patterns like `s3.*.aws.crossplane.io` or `*.s3.*` aren't valid.
{{</hint>}}
{{<hint "tip">}}
You can mix exact names and wildcards for flexible activation:
```yaml
spec:
activate:
- buckets.s3.aws.crossplane.io # Exact S3 buckets
- "*.ec2.aws.crossplane.io" # All EC2 resources
- clusters.eks.aws.crossplane.io # Exact EKS clusters
```
{{</hint>}}
## Common activation strategies
### Activate everything (default behavior)
The Crossplane Helm chart creates a default MRAP that activates all resources:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: default
spec:
activate:
- "*" # Activate all MRDs
```
You can customize this during installation:
```shell
# Disable default activations entirely
helm install crossplane crossplane-stable/crossplane \
--set provider.defaultActivations={}
# Or provide custom default activations
helm install crossplane crossplane-stable/crossplane \
--set provider.defaultActivations={\
"*.s3.aws.crossplane.io","*.ec2.aws.crossplane.io"}
```
### Provider-specific activation
Activate all resources from specific providers:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: aws-provider-resources
spec:
activate:
- "*.aws.crossplane.io" # All AWS resources
- "*.aws.m.crossplane.io" # All AWS managed resources (v2 style)
```
### Service-specific activation
Activate resources for specific cloud services:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: storage-and-compute
spec:
activate:
- "*.s3.aws.crossplane.io" # AWS S3 resources
- "*.ec2.aws.crossplane.io" # AWS EC2 resources
- "*.storage.gcp.crossplane.io" # GCP Storage resources
- "*.compute.gcp.crossplane.io" # GCP Compute resources
```
### Minimal activation
Activate only the resources you know you need:
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: minimal-footprint
spec:
activate:
- buckets.s3.aws.crossplane.io # Just S3 buckets
- instances.ec2.aws.crossplane.io # Just EC2 instances
- databases.rds.aws.crossplane.io # Just RDS databases
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## Multiple MRAPs
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
You can have multiple MRAPs in your cluster. Crossplane processes all MRAPs
together and activates any MRD that matches at least one pattern.
### Team-based activation
Different teams can manage their own activation policies:
```yaml
# Storage team MRAP
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: storage-team
spec:
activate:
- "*.s3.aws.crossplane.io"
- "*.storage.gcp.crossplane.io"
---
# Database team MRAP
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: database-team
spec:
activate:
- "*.rds.aws.crossplane.io"
- "*.sql.gcp.crossplane.io"
```
### Configuration package activation
Configuration packages can include MRAPs to declare their resource dependencies:
```yaml
# In your Configuration package
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceActivationPolicy
metadata:
name: web-platform-dependencies
spec:
activate:
- buckets.s3.aws.crossplane.io # For static assets
- instances.ec2.aws.crossplane.io # For web servers
- databases.rds.aws.crossplane.io # For application data
- certificates.acm.aws.crossplane.io # For HTTPS
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## Working with MRAPs
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### Creating MRAPs
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
Apply an MRAP like any Kubernetes resource:
```shell
kubectl apply -f my-activation-policy.yaml
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### Viewing MRAPs
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
List all MRAPs:
```shell
kubectl get managedresourceactivationpolicies
```
View MRAP details and status:
```shell
kubectl describe mrap aws-core-resources
```
### Checking activation status
MRAPs track which resources they've activated:
```yaml
status:
conditions:
- type: Healthy
status: "True"
reason: Running
activated:
- buckets.s3.aws.crossplane.io
- instances.ec2.aws.crossplane.io
- instances.rds.aws.crossplane.io
- securitygroups.ec2.aws.crossplane.io
- subnets.ec2.aws.crossplane.io
- vpcs.ec2.aws.crossplane.io
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## MRAP status conditions
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
### Healthy condition
- **`Healthy: True, Reason: Running`**: MRAP works
- **`Healthy: Unknown, Reason: EncounteredErrors`**: Some MRDs failed to
activate
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## Troubleshooting MRAPs
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### MRAP exists but resources aren't activated
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
<!-- vale Google.Colons = NO -->
**Symptoms**: MRAP shows `activated: []` or missing expected resources
<!-- vale Google.Colons = YES -->
**Causes and solutions:**
1. **Pattern doesn't match MRD names**
```shell
# List available MRDs
kubectl get mrds
# Check your pattern matches
kubectl get mrds -o name | grep "your-pattern"
```
2. **MRDs don't exist yet**
- Install the required provider first
- Providers create MRDs when they start
3. **Provider doesn't support activation**
```shell
# Check provider capabilities
kubectl get providerrevision <provider-revision-name> \
-o jsonpath='{.status.capabilities}'
# Look for "safe-start"
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### MRAP shows activation errors
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = YES -->
<!-- vale Google.Colons = NO -->
**Symptoms**: MRAP has `Healthy: Unknown` status with errors
<!-- vale Google.Colons = YES -->
**Status condition example:**
```yaml
conditions:
- type: Healthy
status: "Unknown"
reason: EncounteredErrors
message: "failed to activate 2 of 5 ManagedResourceDefinitions"
```
**Solution**: select MRAP events for specific failure details:
```shell
kubectl describe mrap <name>
# Look at the Events section for activation errors
```
### Resources activate when you don't expect them to
**Symptoms**: more resources are active than expected
**Cause**: multiple MRAPs with overlapping patterns (this is normal behavior)
**Solution**: review all MRAP patterns to understand which policies are
activating which resources
```shell
# List all MRAP activation patterns
kubectl get mrap \
-o jsonpath='{range .items[*]}{.metadata.name}: {.spec.activate}{"\n"}{end}'
# Check which MRAPs activated each resource
kubectl get mrap \
-o jsonpath='{range .items[*]}{.metadata.name}: {.status.activated}{"\n"}{end}'
```
## Best practices
MRAPs are additive - multiple MRAPs can activate the same resource without
conflicts. This enables team-based activation strategies and Configuration
package dependencies.
<!-- vale alex.ProfanityUnlikely = NO -->
1. **Start specific, broaden as needed** - Begin with exact resource names,
add wildcards only when beneficial for maintainability
2. **Plan for provider evolution** - Design wildcard patterns that
accommodate new resources as providers add them (for example,
`*.s3.aws.crossplane.io` works for future S3 resources)
3. **Group related resources logically** - Create MRAPs that activate
resources teams actually use together
4. **Include activation dependencies in Configuration packages** -
Configuration packages should declare what MRDs they need rather than
assuming resources are available
5. **Use conservative patterns in shared environments** - Avoid overly broad
wildcards that activate unnecessary resources when multiple teams share
providers
<!-- vale alex.ProfanityUnlikely = YES -->
## Next steps
- Learn about
[ManagedResourceDefinitions]({{<ref "managed-resource-definitions">}})
to understand what MRAPs activate
- See the
[disabling unused managed resources guide]({{<ref "../guides/disabling-unused-managed-resources">}})
for step-by-step implementation
- Check the [API reference]({{<ref "../api">}}) for complete MRAP schema
documentation

View File

@ -0,0 +1,377 @@
---
title: Managed Resource Definitions
weight: 15
state: alpha
alphaVersion: 2.0
description: ManagedResourceDefinitions enable selective activation of provider
resources and reduce CRD overhead
---
{{<hint "important">}}
Crossplane v2.0+ enables managed resource definitions by default. This
automatically converts provider CRDs to MRDs during installation. To disable
this
behavior, set `--enable-custom-to-managed-resource-conversion=false` when
installing Crossplane.
{{</hint>}}
A `ManagedResourceDefinition` (MRD) is a lightweight abstraction over
Kubernetes CustomResourceDefinitions (CRDs) that enables selective activation of
managed resources. MRDs solve the problem of providers installing hundreds of
CRDs when you only need one or two, reducing API server overhead and improving
cluster performance.
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## The CRD scaling problem
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
Large Crossplane providers can install 100+ managed resource CRDs. Each CRD
consumes about 3 MiB of API server memory and creates API endpoints that affect
cluster performance:
- **Memory pressure**: Large providers can consume 300+ MiB of API server
memory
- **Slower kubectl operations**: Commands like `kubectl get managed` must query
all custom resource endpoints
- **Increased API server load**: More CRDs mean more API endpoints to serve
- **Unnecessary resource overhead**: Most users only need a subset of provider
resources
MRDs address this by allowing providers to ship resource definitions that only
become active CRDs when explicitly needed.
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## How MRDs work
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
An MRD contains the same schema as a CRD but adds two key fields:
- **`connectionDetails`**: Documents what connection secrets the resource
provides
- **`state`**: Controls whether the underlying CRD exists (`Active` or
`Inactive`)
When an MRD's state is `Inactive`, no CRD exists in the cluster. When
activated, Crossplane creates the corresponding CRD and the provider can start
managing instances of that resource.
```yaml
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: ManagedResourceDefinition
metadata:
name: buckets.s3.aws.crossplane.io
spec:
group: s3.aws.crossplane.io
names:
kind: Bucket
plural: buckets
scope: Cluster
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
forProvider:
type: object
properties:
region:
type: string
versioning:
type: boolean
connectionDetails:
- name: bucket-name
description: The name of the created S3 bucket
- name: region
description: The AWS region where the bucket was created
state: Inactive # Default state - no CRD created yet
```
## Key characteristics
- **Selective activation**: Only create CRDs for resources you actually need
- **Performance benefits**: Inactive MRDs consume minimal cluster resources
- **Connection details documentation**: Schema for documenting available
connection secrets
- **One-way state transition**: MRDs can go from `Inactive` to `Active` but not
back
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## MRD states
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
### Inactive state
When `state: Inactive` (the default):
- No CRD exists in the cluster
- No API endpoints exist
- The provider doesn't start a controller for this resource
- Minimal memory and CPU overhead
```yaml
spec:
state: Inactive # Default for all MRDs
```
### Active state
When `state: Active`:
- Crossplane creates the corresponding CRD
- API endpoints become available for the resource
- The provider starts a controller to manage instances
- Full capability like traditional managed resources
```yaml
spec:
state: Active # CRD will be created
```
{{<hint "important">}}
MRD state transitions are one-way only. Once an MRD becomes `Active`, it can't
return to `Inactive`. This prevents accidental deletion of CRDs that may have
existing resources.
{{</hint>}}
## Connection details documentation
MRDs can document what connection details a managed resource provides. This
helps users understand what data is available in connection secrets without
having to create test resources.
```yaml
spec:
connectionDetails:
- name: endpoint
description: The RDS instance endpoint for database connections
- name: port
description: The port number for database connections
- name: username
description: The master username for database access
- name: password
description: The auto-generated master password
```
{{<hint "note">}}
<!-- vale write-good.Passive = NO -->
<!-- vale gitlab.CurrentStatus = NO -->
<!-- vale write-good.Weasel = NO -->
Connection details are currently a schema-only feature. Most providers
don't yet populate the `connectionDetails` field in their MRDs, but the structure
is available for future implementation.
<!-- vale write-good.Weasel = YES -->
<!-- vale gitlab.CurrentStatus = YES -->
<!-- vale write-good.Passive = YES -->
{{</hint>}}
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## Working with MRDs
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### Viewing MRDs
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
List all MRDs in your cluster:
```shell
kubectl get managedresourcedefinitions
```
View MRD details:
```shell
kubectl describe mrd buckets.s3.aws.crossplane.io
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### Checking MRD status
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
MRDs provide status information about their lifecycle:
```yaml
status:
conditions:
- type: Established
status: "False"
reason: InactiveManagedResource
message: "ManagedResourceDefinition is inactive"
```
**Status conditions:**
- **`Established: False, Reason: InactiveManagedResource`**: MRD is inactive,
no CRD created
- **`Established: Unknown, Reason: PendingManagedResource`**: Crossplane is
creating the CRD
- **`Established: True, Reason: EstablishedManagedResource`**: CRD exists and
is ready
- **`Healthy: True, Reason: Running`**: MRD controller operating
- **`Healthy: Unknown, Reason: EncounteredErrors`**: MRD controller
experiencing issues
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### Manually activating MRDs
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
You can manually activate an MRD by changing its state:
```shell
kubectl patch mrd buckets.s3.aws.crossplane.io --type='merge' \
-p='{"spec":{"state":"Active"}}'
```
The recommended approach is to use
[ManagedResourceActivationPolicies]({{<ref "managed-resource-activation-policies">}})
for systematic activation.
## How providers work with MRDs
Crossplane v2.0+ automatically converts all provider CRDs to MRDs during
package installation, regardless of the provider's age or original format. The
provider's `safe-start` capability determines the default MRD state:
### Providers with `safe-start` capability
- MRDs start with `state: Inactive` by default
- Support selective activation via
[ManagedResourceActivationPolicies]({{<ref "managed-resource-activation-policies">}})
- Reduced resource overhead for unused resources
- Provider can start without all CRDs being active
```yaml
# Provider package metadata
apiVersion: meta.pkg.crossplane.io/v1
kind: Provider
spec:
capabilities:
- safe-start
```
{{<hint "tip">}}
Crossplane uses fuzzy matching for capabilities, so `safe-start`,
`safe_start`, `safestart`, and `SafeStart` all match the `safe-start`
capability.
{{</hint>}}
### Providers without `safe-start` capability
- MRDs start with `state: Active` by default (legacy behavior)
- All CRDs become available for backward compatibility
- Full resource overhead like traditional providers
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
## Troubleshooting MRDs
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### MRD exists but no CRD appears
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Colons = NO -->
**Symptoms**: MRD is present but `kubectl get <resource>` shows "no
resources found"
**Cause**: MRD is in `Inactive` state
**Solution**: Activate the MRD using an
[ManagedResourceActivationPolicy]({{<ref "managed-resource-activation-policies">}})
or manually patch the state
<!-- vale Google.Colons = YES -->
```shell
# Check MRD state
kubectl get mrd <name> -o jsonpath='{.spec.state}'
# Activate if needed
kubectl patch mrd <name> --type='merge' -p='{"spec":{"state":"Active"}}'
```
<!-- vale Google.Headings = NO -->
<!-- vale Microsoft.HeadingAcronyms = NO -->
### MRD activation fails
<!-- vale Google.Headings = YES -->
<!-- vale Microsoft.HeadingAcronyms = YES -->
<!-- vale Google.Colons = NO -->
**Symptoms**: MRD state is `Active` but `Established` condition remains `False`
**Cause**: CRD creation failed due to schema issues or conflicts
**Solution**: Check MRD events and status for error details
<!-- vale Google.Colons = YES -->
```shell
kubectl describe mrd <name>
```
**Other status conditions for troubleshooting:**
- **`Established: False, Reason: BlockedManagedResourceActivationPolicy`**:
Blocked by activation policy issues
- **`Established: False, Reason: TerminatingManagedResource`**: Crossplane is
deleting the MRD
**Common events you might see:**
- `Normal CreateCustomResourceDefinition` - CRD successfully created
- `Normal UpdateCustomResourceDefinition` - CRD successfully updated
- `Warning CreateCustomResourceDefinition` - CRD creation failed
- `Warning UpdateCustomResourceDefinition` - CRD update failed
- `Warning Reconcile` - General reconciliation errors
Common issues:
- Malformed OpenAPI schema in the MRD
- CRD name conflicts with existing resources
- Insufficient RBAC permissions for Crossplane
### Provider doesn't support activation
<!-- vale Google.Colons = NO -->
**Symptoms**: Provider starts all controllers regardless of MRD states
**Cause**: Provider doesn't implement late activation support
**Solution**: Check provider capabilities and use a compatible provider version
<!-- vale Google.Colons = YES -->
```shell
# Check if provider supports late activation
kubectl get providerrevision <provider-revision-name> \
-o jsonpath='{.status.capabilities}'
```
Look for the `safe-start` capability.
## Next steps
- Learn about
[ManagedResourceActivationPolicies]({{<ref "managed-resource-activation-policies">}})
for systematic resource activation
- See the
[disabling unused managed resources guide]({{<ref "../guides/disabling-unused-managed-resources">}})
for practical implementation
- Check the [API reference]({{<ref "../api">}}) for complete MRD schema
documentation

View File

@ -454,6 +454,8 @@ password: 27 bytes
{{<hint "important" >}} {{<hint "important" >}}
The Provider determines the data written to the Secret object. Refer to the The Provider determines the data written to the Secret object. Refer to the
specific Provider documentation for the generated Secret data. specific Provider documentation for the generated Secret data.
[ManagedResourceDefinitions]({{<ref "managed-resource-definitions">}}) can document what connection details a managed resource provides, though most providers don't yet populate this information.
{{< /hint >}} {{< /hint >}}
## Annotations ## Annotations

View File

@ -171,6 +171,11 @@ deprecate and remove cluster scoped MRs at a future date.
Read more about Crossplane v2's [backward compatibility](#backward-compatibility). Read more about Crossplane v2's [backward compatibility](#backward-compatibility).
{{</hint>}} {{</hint>}}
Crossplane v2 also introduces
[managed resource definitions]({{<ref "../managed-resources/managed-resource-definitions">}})
for selective activation of provider resources, reducing cluster overhead by
installing only the managed resources you actually need.
## Compose any resource ## Compose any resource
Crossplane v2 isn't opinionated about using composition together with managed Crossplane v2 isn't opinionated about using composition together with managed

View File

@ -27,6 +27,7 @@ CronJobs
crt crt
CSS CSS
CUE CUE
CustomResourceDefinitions
CVEs CVEs
DatabaseInstance DatabaseInstance
DevOps DevOps
@ -41,6 +42,7 @@ ESS
float64 float64
GitOps GitOps
Go Go
godoc
gRPC gRPC
hostname hostname
IAM IAM
@ -56,7 +58,9 @@ kube-controller-manager
kubectl kubectl
kv kv
KV KV
main.go
metrics-server metrics-server
MiB
minikube minikube
multi-platform multi-platform
namespace namespace

View File

@ -28,6 +28,7 @@ Crossplane
crossplane-admin crossplane-admin
crossplane-browse crossplane-browse
crossplane-edit crossplane-edit
crossplane-runtime
Crossplane's Crossplane's
crossplane-view crossplane-view
crossplane.yaml crossplane.yaml
@ -60,8 +61,17 @@ InactivePackageRevision
initProvider initProvider
KCL KCL
LateInitialize LateInitialize
ManagedResourceActivationPolicies
ManagedResourceActivationPolicy
ManagedResourceDefinition
ManagedResourceDefinitions
managementPolicies managementPolicies
MR MR
MRAP
MRAPs
MRD
MRD's
MRDs
MRs MRs
Operation-specific Operation-specific
PatchSet PatchSet

View File

@ -45,16 +45,22 @@ non-Kubernetes
non-production non-production
one-time one-time
One-time One-time
one-way
One-way
Operation-level Operation-level
pattern-based
Pattern-based
per-element per-element
performant performant
per-object per-object
per-resource per-resource
poll-interval poll-interval
pre-existing pre-existing
prefix-only
preload preload
pre-provisioned pre-provisioned
pre-release pre-release
Provider-specific
race-conditions race-conditions
read-only read-only
ready-made ready-made
@ -62,8 +68,11 @@ resource-intensive
resource-specific resource-specific
right-hand right-hand
run-time run-time
safe-start
schema-only
self-service self-service
self-signed self-signed
Service-specific
space-delimited space-delimited
status-checking status-checking
step-by-step step-by-step
@ -71,6 +80,8 @@ subresources
System-level System-level
/tab /tab
/tabs /tabs
team-based
Team-based
third-party third-party
Time-sensitive Time-sensitive
top-level top-level