Crossplane’s deletion policy on managed resources is a critical safety net that prevents accidental destruction of your cloud infrastructure.

Let’s see this in action. Imagine you have a Kubernetes CompositeResourceDefinition (CRD) for a ProductionDatabase that looks something like this:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: productiondatabases.database.example.com
spec:
  group: database.example.com
  names:
    kind: ProductionDatabase
    plural: productiondatabases
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    storageGB:
                      type: integer
              required:
                - parameters
            status:
              type: object
              properties:
                dbInstanceName:
                  type: string

And you instantiate it with a CompositeResource (XR):

apiVersion: database.example.com/v1alpha1
kind: ProductionDatabase
metadata:
  name: my-production-db
spec:
  parameters:
    storageGB: 100

When Crossplane reconciles this XR, it will create a Composite resource, which in turn provisions a ManagedResource (MR) like a RDSInstance from the AWS provider. This RDSInstance is the actual cloud resource.

The deletionPolicy is a field you set on the CompositeResource (XR) itself. It dictates what happens to the underlying ManagedResource (and thus the cloud resource) when the XR is deleted.

Here are the common policies and how they work:

  • Orphan (Default): This is the safest default. When you delete the XR, the underlying MR and its cloud resource are not deleted. They are orphaned, meaning they continue to exist independently. This is great for protecting critical infrastructure, but you’ll need to manually clean up the orphaned resources later.

    Consider this XR with the default policy:

    apiVersion: database.example.com/v1alpha1
    kind: ProductionDatabase
    metadata:
      name: my-production-db
    spec:
      parameters:
        storageGB: 100
    

    If you kubectl delete productiondatabase my-production-db, the RDSInstance provisioned by Crossplane will remain in AWS.

  • Foreground: This policy ensures that the XR is deleted only after all its owned MRs are successfully deleted. Crossplane will delete the MRs in the order specified by their deletionPolicy (which defaults to Foreground if not specified on the MR itself). If any MR fails to delete, the XR deletion will be blocked. This provides a controlled, cascading deletion.

    To enforce this, you’d explicitly set it on your XR:

    apiVersion: database.example.com/v1alpha1
    kind: ProductionDatabase
    metadata:
      name: my-production-db
      annotations:
        crossplane.io/deletion-policy: Foreground # Or set it directly in spec
    spec:
      parameters:
        storageGB: 100
    

    When you delete this XR, Crossplane will first try to delete the associated RDSInstance. Only upon successful deletion of the RDSInstance will the ProductionDatabase XR be removed from Kubernetes.

  • Delete: This is the most aggressive policy. When the XR is deleted, Crossplane immediately attempts to delete all its owned MRs and their corresponding cloud resources. This is useful for ephemeral environments or when you want complete cleanup.

    Setting this policy on your XR:

    apiVersion: database.example.com/v1alpha1
    kind: ProductionDatabase
    metadata:
      name: my-production-db
      annotations:
        crossplane.io/deletion-policy: Delete # Or set it directly in spec
    spec:
      parameters:
        storageGB: 100
    

    Deleting this XR will trigger the deletion of the RDSInstance and its underlying AWS RDS instance.

The deletionPolicy can also be applied directly to Managed Resources (MRs) like RDSInstance if you need finer-grained control over specific cloud resources, overriding the policy set on the XR. However, setting it on the XR is usually sufficient and more manageable.

When a CompositeResource (XR) is deleted, Crossplane doesn’t just delete the Managed Resources (MRs) it owns. It iterates through the Composition that created the XR, identifies the MRs defined in the resources field of the Composite template, and then applies the deletion policy to those specific MRs. If an MR has its own deletionPolicy set, that takes precedence over the XR’s policy for that specific MR.

The most surprising thing about the deletionPolicy is that it’s not just about deleting resources; it’s fundamentally about managing the lifecycle of your cloud infrastructure as a declarative Kubernetes object. The Orphan policy, by default, ensures that Kubernetes’ imperative delete command on an XR doesn’t lead to an irreversible cloud resource deletion, forcing a more deliberate operational process for infrastructure decommissioning.

You might find that even with Foreground or Delete policies, resources linger. This often happens when the cloud provider’s API is slow to confirm deletion, or if there are external dependencies on the resource that prevent immediate removal. In such cases, Crossplane will continue to attempt deletion, but the XR might appear deleted while its underlying resources are still being cleaned up by the provider.

The next concept you’ll likely encounter is how to manage resource updates and drifts, particularly when using Orphan or Foreground deletion policies.

Want structured learning?

Take the full Crossplane course →