Course contentsShow
← Docker, Kubernetes & CI/CD
Lesson 489 of 8027. Package Management and AutomationFree lesson

Implementing Finalizers for Resource Cleanup

Add custom finalizer logic to your controller to perform graceful teardown of external resources before a custom resource is removed.

In this lesson, you will learn how to use finalizers to prevent your Custom Resources from being deleted until your controller has successfully cleaned up associated external infrastructure.

The Problem: Orphaned Infrastructure

By default, when you delete a Kubernetes Custom Resource (CR), Kubernetes removes the object immediately. If that CR was responsible for an external resource—like a cloud database or a DNS entry—that external resource becomes "orphaned." Because the Kubernetes object is gone, your controller no longer has a reference to track or delete that external dependency.

The Solution: Finalizers

A finalizer is a simple string (a key) that you add to the metadata.finalizers slice of your resource. When a finalizer is present, Kubernetes changes the behavior of the delete request: instead of removing the object, it marks the resource for deletion by setting a deletionTimestamp and prevents the garbage collector from finalizing the removal until that slice is empty.

Implementing the Cleanup Flow

Your controller’s Reconcile loop needs to handle this logic in three steps:

  1. Check for Deletion: Look at object.GetDeletionTimestamp(). If it is non-zero, the user has requested a deletion.
  2. Execute Cleanup: If the finalizer is present, perform your teardown logic (e.g., calling an API to delete a cloud database).
  3. Remove the Finalizer: Once the external cleanup succeeds, remove the finalizer string from the metadata.finalizers slice and update the object. Kubernetes will then see the empty slice and complete the deletion of the CR.
// Inside your Reconcile loop
if !myCR.ObjectMeta.DeletionTimestamp.IsZero() {
    if containsString(myCR.Finalizers, myFinalizerName) {
        // Perform the external cleanup here
        if err := deleteExternalResource(myCR); err != nil {
            return ctrl.Result{}, err // Requeue if cleanup fails
        }

        // Remove finalizer to allow Kubernetes to delete the CR
        controllerutil.RemoveFinalizer(myCR, myFinalizerName)
        return ctrl.Result{}, r.Update(ctx, myCR)
    }
    return ctrl.Result{}, nil
}

By ensuring your controller verifies the external state before "releasing" the CR for deletion, you maintain full control over the lifecycle of your infrastructure, preventing costly or messy residue in your environment.

Key Takeaway: Finalizers intercept the deletion process, allowing your controller to perform required teardown tasks before Kubernetes permits the Custom Resource to be garbage collected.