Crossplane logo
Crossplane logo
  • Why Control Planes?
  • Documentation
  • Community
  • Blog
  • Crossplane GitHub
  • Crossplane Slack
Crossplane Documentation - v
Welcome
What's Crossplane?
What's New in v2?
Get Started
Install Crossplane
Get Started With Composition
Get Started With Managed Resources
Get Started With Operations
Composition
Composite Resources
Composite Resource Definitions
Compositions
Composition Revisions
Environment Configs
Managed Resources
Managed Resources
Managed Resource Definitions
Managed Resource Activation Policies
Usages
Operations
Operations
Cron Operations
Watch Operations
Packages
Providers
Functions
Configurations
Image Configs
Guides
Crossplane Pods
Metrics
Function Patch and Transform
Releasing Crossplane Extensions
Write a Composition Function in Go
Write a Composition Function in Python
Disabling Unused Managed Resources
Implementing safe-start in Providers
Troubleshoot Crossplane
Upgrade Crossplane
Upgrade to Crossplane v2
Uninstall Crossplane
CLI Reference
Command Reference
API Reference
Learn More
Release Cycle
Feature Lifecycle
Contributing Guide
Crossplane Roadmap
v2.0
Latest
master v2.0-preview v2.0
Latest
v1.20 v1.19

Implementing safe-start in Providers

This is an alpha feature. Crossplane may change or drop this feature at any time.

This feature was introduced in v2.

For more information read the Crossplane feature lifecycle.

On this page
  • What safe-start provides
  • Prerequisites
  • Implementation steps
    • Step 1: Declare safe-start capability
    • Step 2: Add required imports
    • Step 3: Initialize the gate
    • Step 4: Use gated controller setup
    • Step 5: Update controller registration
  • Implementation details
  • Testing your implementation
  • Troubleshooting
    • Controllers never start
    • CRDs don’t appear
  • Migration considerations
  • Next steps
Report a problem
View page source

This guide shows provider developers how to implement safe-start capability in their Crossplane providers. safe-start enables disabling unused managed resources through ManagedResourceDefinitions, improving performance and reducing resource overhead.

Important
safe-start requires Crossplane v2.0+ and crossplane-runtime v2.0+. Implementing safe-start involves code changes that affect provider startup behavior.

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
  • Test environment with Crossplane v2.0+

Implementation steps

Step 1: Declare safe-start capability

Add safe-start to your provider package metadata:

1# package/crossplane.yaml 2apiVersion: meta.pkg.crossplane.io/v1 3kind: Provider 4metadata: 5  name: provider-example 6spec: 7  capabilities: 8  - safe-start 

Step 2: Add required imports

Update your main.go imports (see crossplane-runtime godoc for full API reference):

 1import (  2    // existing imports...  3      4    "k8s.io/apimachinery/pkg/runtime/schema"  5    apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"  6      7    "github.com/crossplane/crossplane-runtime/v2/pkg/controller"  8    "github.com/crossplane/crossplane-runtime/v2/pkg/gate"  9    "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/customresourcesgate" 10) 

Step 3: Initialize the gate

Add gate initialization in your main function:

 1func main() {  2    // existing setup code...  3      4    o := controller.Options{  5        // existing options...  6        Gate: new(gate.Gate[schema.GroupVersionKind]),  7    }  8      9    // Add CustomResourceDefinition to scheme for gate controller 10    if err := apiextensionsv1.AddToScheme(mgr.GetScheme()); err != nil { 11        panic(err) 12    } 13     14    // Setup controllers 15    if err := yourprovider.Setup(mgr, o); err != nil { 16        panic(err) 17    } 18         19    // Setup the CRD gate controller   20    if err := customresourcesgate.Setup(mgr, o); err != nil { 21        panic(err) 22    } 23         24    // start manager... 25} 

Step 4: Use gated controller setup

Create a gated setup function for each managed resource controller:

 1// SetupGated registers controller setup with the gate, waiting for the  2// required CRD  3func SetupGated(mgr ctrl.Manager, o controller.Options) error {  4    o.Gate.Register(func() {  5        if err := Setup(mgr, o); err != nil {  6            panic(err)  7        }  8    }, v1alpha1.MyResourceGroupVersionKind)  9    return nil 10} 11 12// Setup is your existing controller setup function (unchanged) 13func Setup(mgr ctrl.Manager, o controller.Options) error { 14    // existing controller setup code... 15} 

Step 5: Update controller registration

Change your controller setup to use the gated versions:

 1// internal/controller/controller.go  2func Setup(mgr ctrl.Manager, o controller.Options) error {  3    for _, setup := range []func(ctrl.Manager, controller.Options) error{  4        myresource.SetupGated,  // Changed from myresource.Setup  5        // other gated setups...  6    } {  7        if err := setup(mgr, o); err != nil {  8            return err  9        } 10    } 11    return nil 12} 

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:

 1# Install Crossplane v2.0+  2helm install crossplane crossplane-stable/crossplane \  3  --namespace crossplane-system \  4  --set provider.defaultActivations={}  5  6# Install your provider    7kubectl apply -f provider.yaml  8  9# Check that MRDs are created but inactive 10kubectl get mrds 11# All should show STATE: Inactive 12 13# No CRDs should exist yet 14kubectl get crds | grep yourprovider.m.crossplane.io 15# Should return no results 16 17# Create activation policy 18kubectl apply -f - <<EOF 19apiVersion: apiextensions.crossplane.io/v1alpha1 20kind: ManagedResourceActivationPolicy 21metadata: 22  name: test-activation 23spec: 24  activate: 25  - "myresource.yourprovider.m.crossplane.io" 26EOF 27 28# Verify activation worked 29kubectl get mrd myresource.yourprovider.m.crossplane.io 30# Should show STATE: Active 31 32# CRD should now exist 33kubectl get crd myresource.yourprovider.m.crossplane.io 

Troubleshooting

Controllers never start

Cause: gate waits for CRDs that never become active.

Solution: check that Crossplane activated MRDs and created CRDs:

1kubectl get mrds -o wide 2kubectl describe mrap <activation-policy-name> 

CRDs don’t appear

Cause: MRDs might not activate or activation policy doesn’t match.

Solution: verify activation policy patterns match MRD names:

1kubectl get mrds 2kubectl 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.

Crossplane logo
Twitter
Youtube
Podcast
Forum

© Crossplane Authors 2025. Documentation distributed under CC-BY-4.0.

© 2025 The Linux Foundation. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.

cncfLogo

We are a Cloud Native Computing Foundation incubating project.